基于Java的Web应用开发:Servlet与JSP实践
1. JSP与Servlet基础
JSP(JavaServer Pages)与Servlet都是Java用于开发Web应用的重要技术。JSP本质上是嵌入了Java代码的HTML文档,而Servlet则是嵌入了HTML的Java代码,二者各有优势。例如,JSP对于网页设计师来说更容易编辑,而Servlet则更适合处理业务逻辑。
以下是一个简单的JSP示例
hello.jsp
,它使用
Rete
对象打印 “Hello World from Jess!” 消息:
<html>
<%@ page import="jess.*" %>
<head>
<title>Hello World!</title>
</head>
<body>
<H1><%
Rete engine = new Rete();
engine.addOutputRouter("page", out);
engine.executeCommand("(printout page " +
"\"Hello World from Jess!\" crlf)");
%></H1>
</body>
</html>
在这个示例中,
out
变量等同于
Hello
Servlet 中
response.getWriter()
的返回值,并且所有JSP都可以自动使用它。
要在Tomcat中安装JSP,只需将源代码复制到Web应用目录的顶层。Tomcat会识别JSP扩展名,首次请求该文件时,它会被编译成Servlet并执行。
2. Servlet与JSP协作
Servlet和JSP可以轻松协作。通常,Servlet执行计算,将结果存储在
HttpServletRequest
对象中,然后将增强后的请求对象转发给JSP。JSP从
HttpServletRequest
中获取所需数据并生成HTML页面。
以下是一个Servlet将
Rete
对象传递给JSP的示例:
Rete engine = new Rete();
request.setAttribute("engine", engine);
ServletContext servletContext = getServletContext();
RequestDispatcher dispatcher =
servletContext.getRequestDispatcher("/hello.jsp");
dispatcher.forward(request, response);
return;
对应的JSP代码如下:
<html>
<%@ page import="jess.*" %>
<jsp:useBean id="engine" class="jess.Rete" scope="request"/>
<head>
<title>Hello World!</title>
</head>
<body>
<H1><%
engine.addOutputRouter("page", out);
engine.executeCommand("(printout page " +
"\"Hello World from Jess!\" crlf)");
%></H1>
</body>
</html>
jsp:useBean
标签会自动从
HttpServletRequest
中提取
Rete
对象,并将其分配给正确类型的变量。
为了方便调用,可以将JSP转发机制封装到一个方法中:
protected void dispatch(HttpServletRequest request,
HttpServletResponse response,
String page)
throws IOException, ServletException {
ServletContext servletContext = getServletContext();
RequestDispatcher dispatcher =
servletContext.getRequestDispatcher(page);
dispatcher.forward(request, response);
}
使用
dispatch
方法后,前面的Servlet代码可以简化为:
Rete engine = new Rete();
request.setAttribute("engine", engine);
dispatch(request, response, "/hello.jsp");
return;
3. 应用架构概述
在开始实现之前,我们先重新审视一下应用架构。以下是构成推荐代理应用的各个文件及其描述:
| 源文件 | 描述 |
|------------------|--------------------------------------------------------------|
| index.html | 简单的登录屏幕 |
| Catalog.java | 创建订单对象并列出产品的Servlet |
| catalog.jsp | 显示产品表和订购表单的JSP |
| Recommend.java | 接受提交的订单表单并运行推荐代理的Servlet |
| recommend.jsp | 显示推荐信息并提供将推荐添加到订单的表单的JSP |
| Purchase.java | 接受已完成订单的Servlet |
| purchase.jsp | 最终屏幕 |
在这个架构中,我们主要关注Servlet和JSP中的Java代码,JSP中的HTML部分通常由网页设计师进行完善。
4. 登录屏幕
客户看到的第一个页面是登录屏幕。这里我们使用一个简单的HTML表单,让客户输入一个字符串作为客户ID:
<HTML>
<HEAD>
<TITLE>Welcome to TekMart.com!</TITLE>
</HEAD>
<BODY>
<H1>Welcome to TekMart.com</H1>
Welcome to the TekMart.com online store!
<P>
To get started, please enter your customer id:
<FORM action="/Order/catalog" method="POST">
<INPUT name="customerId">
<INPUT type="submit" value="Log in">
</FORM>
</P>
</BODY>
</HTML>
客户可以在输入字段中输入登录名,然后点击 “Log in” 按钮。表单提交结果将发送到
action
属性指定的URL,这里是
/Order/catalog
。
5. Catalog Servlet
Catalog Servlet需要完成以下任务:
1. 获取登录屏幕上输入的登录名;如果没有输入,则返回登录屏幕。
2. 调用
get-new-order-number
获取唯一的订单号。
3. 使用登录名和订单号创建新的用户会话。
4. 使用
all-products
查询获取产品事实列表。
5. 将结果转发给JSP进行显示。
以下是Catalog Servlet的详细实现:
5.1 初始化Jess
public class Catalog extends BaseServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
checkInitialized();
为了提高效率,所有客户将共享一个
Rete
对象。
checkInitialized
方法用于检查
Rete
对象是否已初始化,如果未初始化,则创建并初始化一个新的
Rete
对象:
protected void checkInitialized()
throws ServletException {
ServletContext servletContext = getServletContext();
String rulesFile =
servletContext.getInitParameter("rulesfile");
String factsFile =
servletContext.getInitParameter("factsfile");
if (servletContext.getAttribute("engine") == null) {
try {
Rete engine = new Rete(this);
new Batch().batch(rulesFile, engine);
engine.reset();
if (new File(factsFile).exists())
engine.executeCommand("(load-facts " +
factsFile + ")");
servletContext.setAttribute("engine", engine);
} catch (Exception je) {
throw new ServletException(je);
}
}
}
BaseServlet
类还实现了
doPost
方法,让所有Servlet可以免费获得对POST请求的处理能力:
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doGet(request, response);
}
5.2 获取登录名
String customerId =
(String) request.getParameter("customerId");
if (customerId == null || customerId.length() == 0) {
dispatch(request, response, "/index.html");
return;
}
如果没有登录名,Servlet将调用登录屏幕。
5.3 启动用户会话
request.getSession().invalidate();
HttpSession session = request.getSession();
session.setAttribute("customerId", customerId);
session.setAttribute("orderNumber",
String.valueOf(getNewOrderNumber()));
getNewOrderNumber
方法调用共享
Rete
对象上的
defquery
来获取下一个订单号:
private int getNewOrderNumber() throws JessException {
ServletContext servletContext = getServletContext();
Rete engine = (Rete) servletContext.getAttribute("engine");
String command = "(get-new-order-number)";
int nextOrderNumber =
engine.executeCommand(command).intValue(null);
return nextOrderNumber;
}
5.4 查询产品列表
ServletContext servletContext = getServletContext();
Rete engine = (Rete) servletContext.getAttribute("engine");
Iterator result =
engine.runQuery("all-products", new ValueVector());
request.setAttribute("queryResult", result);
这里使用了三种不同的方式在Web应用的各个部分之间共享信息:
-
Rete
对象存储在
ServletContext
中,供所有Servlet和JSP在整个Web应用生命周期内共享。
- 登录名和订单号存储在
HttpSession
中,仅在单个浏览器会话中可用。
- 使用
HttpServletRequest
将数据从Servlet传递给JSP,该数据仅在响应单个浏览器请求时存在。
5.5 调用JSP
try {
// 前面的代码
} catch (JessException je) {
throw new ServletException(je);
}
dispatch(request, response, "/catalog.jsp");
如果出现异常,将抛出
ServletException
,Tomcat会返回一个错误页面。
5.6 Catalog JSP
Catalog JSP用于显示Catalog Servlet准备的产品列表:
<HTML>
<%@ page import="jess.*" %>
<jsp:useBean id="queryResult"
class="java.util.Iterator" scope="request"/>
<HEAD>
<TITLE>Ordering from Tekmart.com</TITLE>
</HEAD>
<BODY>
<H1>Tekmart.com Catalog</H1>
Select the items you wish to purchase and press
"Check Order" to continue.
<FORM action="/Order/recommend" method="POST">
<TABLE border="1">
<TR>
<TH>Name</TH>
<TH>Catalog #</TH>
<TH>Price</TH>
<TH>Purchase?</TH>
</TR>
<% while (queryResult.hasNext()) {
Token token = (Token) queryResult.next();
Fact fact = token.fact(1);
String partNum =
fact.getSlotValue("part-number")
.stringValue(null); %>
<TR>
<TD><%= fact.getSlotValue("name")
.stringValue(null) %></TD>
<TD><%= partNum %></TD>
<TD><%= fact.getSlotValue("price")
.floatValue(null) %></TD>
<TD><INPUT type="checkbox" name="items"
value=<%= '"’ + partNum + '"'%>></TD>
</TR>
<% } %>
</TABLE>
<INPUT type="submit" value="Check Order">
</FORM>
</BODY>
</HTML>
这个JSP使用Java的
while
循环渲染产品表格,每个产品对应一行,包含产品名称、目录号、价格和一个复选框。
6. 测试
使用Web浏览器测试Web应用既繁琐又容易出错。幸运的是,有许多自动化测试工具可供选择,主要分为两类:
- 负载测试工具:模拟大量同时访问您网站的用户。
- 页面测试工具:调用特定URL并检查生成的页面是否正确。
对于开发者来说,页面测试工具在早期阶段更有用。这里推荐使用开源的
HttpUnit/ServletUnit
框架进行测试。在开始编写和测试大型应用之前,建议先使用Jess的命令行界面尽可能仔细地测试和调试规则。
7. Recommend Servlet
当登录表单提交时,Catalog Servlet会获取一个名为
customerId
的请求参数。当目录表单提交时,每个客户购买的产品都会有一个
items
请求参数,这些参数将作为Recommend Servlet的输入。
Recommend Servlet需要完成以下任务:
1. 收集商品、客户ID和订单号。
2. 确保客户已登录并正在订购商品。
3. 重置订单,以防表单被重新提交。
4. 创建订单事实。
以上就是基于Java的Web应用开发中Servlet和JSP的详细介绍和实践,通过合理使用这些技术,可以构建出高效、可维护的Web应用。
基于Java的Web应用开发:Servlet与JSP实践
8. Recommend Servlet详细实现
8.1 收集信息
首先,Recommend Servlet需要从请求和会话中收集商品、客户ID和订单号。以下是示例代码:
public class Recommend extends BaseServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 获取客户ID和订单号
HttpSession session = request.getSession();
String customerId = (String) session.getAttribute("customerId");
String orderNumber = (String) session.getAttribute("orderNumber");
// 获取客户购买的商品
String[] items = request.getParameterValues("items");
在这段代码中,我们从
HttpSession
中获取客户ID和订单号,从
HttpServletRequest
中获取客户购买的商品列表。
8.2 验证客户状态
接下来,需要确保客户已登录并正在订购商品:
if (customerId == null || customerId.length() == 0 || items == null || items.length == 0) {
dispatch(request, response, "/index.html");
return;
}
如果客户未登录或没有选择商品,将重定向到登录页面。
8.3 重置订单
为了防止表单被重新提交,需要重置订单:
ServletContext servletContext = getServletContext();
Rete engine = (Rete) servletContext.getAttribute("engine");
try {
engine.executeCommand("(reset-order " + orderNumber + ")");
} catch (JessException je) {
throw new ServletException(je);
}
这里调用了
reset-order
函数来重置订单。
8.4 创建订单事实
最后,根据收集到的信息创建订单事实:
for (String item : items) {
try {
engine.executeCommand("(create-order-fact " + orderNumber + " " + item + ")");
} catch (JessException je) {
throw new ServletException(je);
}
}
通过遍历商品列表,为每个商品创建订单事实。
8.5 调用推荐逻辑并转发到JSP
完成上述步骤后,运行推荐逻辑并将结果转发到
recommend.jsp
:
try {
Iterator result = engine.runQuery("recommendations", new ValueVector());
request.setAttribute("recommendations", result);
} catch (JessException je) {
throw new ServletException(je);
}
dispatch(request, response, "/recommend.jsp");
}
}
这里运行了
recommendations
查询,并将结果存储在
HttpServletRequest
中,然后转发到
recommend.jsp
进行显示。
9. Recommend JSP
recommend.jsp
用于显示推荐信息并提供将推荐添加到订单的表单:
<HTML>
<%@ page import="jess.*" %>
<jsp:useBean id="recommendations"
class="java.util.Iterator" scope="request"/>
<HEAD>
<TITLE>Recommendations from Tekmart.com</TITLE>
</HEAD>
<BODY>
<H1>Tekmart.com Recommendations</H1>
Here are some products we recommend for you:
<FORM action="/Order/purchase" method="POST">
<TABLE border="1">
<TR>
<TH>Name</TH>
<TH>Catalog #</TH>
<TH>Price</TH>
<TH>Add to Order?</TH>
</TR>
<% while (recommendations.hasNext()) {
Token token = (Token) recommendations.next();
Fact fact = token.fact(1);
String partNum =
fact.getSlotValue("part-number")
.stringValue(null); %>
<TR>
<TD><%= fact.getSlotValue("name")
.stringValue(null) %></TD>
<TD><%= partNum %></TD>
<TD><%= fact.getSlotValue("price")
.floatValue(null) %></TD>
<TD><INPUT type="checkbox" name="recommendedItems"
value=<%= '"’ + partNum + '"'%>></TD>
</TR>
<% } %>
</TABLE>
<INPUT type="submit" value="Place Order">
</FORM>
</BODY>
</HTML>
这个JSP使用Java的
while
循环渲染推荐产品表格,每个推荐产品对应一行,包含产品名称、目录号、价格和一个复选框,用户可以选择将推荐产品添加到订单中。
10. Purchase Servlet
Purchase Servlet用于接受已完成的订单:
public class Purchase extends BaseServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();
String customerId = (String) session.getAttribute("customerId");
String orderNumber = (String) session.getAttribute("orderNumber");
String[] items = request.getParameterValues("items");
String[] recommendedItems = request.getParameterValues("recommendedItems");
// 合并普通商品和推荐商品
List<String> allItems = new ArrayList<>();
if (items != null) {
allItems.addAll(Arrays.asList(items));
}
if (recommendedItems != null) {
allItems.addAll(Arrays.asList(recommendedItems));
}
// 处理订单
ServletContext servletContext = getServletContext();
Rete engine = (Rete) servletContext.getAttribute("engine");
try {
engine.executeCommand("(process-order " + orderNumber + " " + String.join(" ", allItems) + ")");
} catch (JessException je) {
throw new ServletException(je);
}
dispatch(request, response, "/purchase.jsp");
}
}
在这个Servlet中,我们首先从请求和会话中获取客户ID、订单号以及用户选择的商品列表,然后合并普通商品和推荐商品,最后调用
process-order
函数处理订单,并将结果转发到
purchase.jsp
。
11. Purchase JSP
purchase.jsp
是最终屏幕,用于显示订单处理结果:
<HTML>
<HEAD>
<TITLE>Order Confirmation</TITLE>
</HEAD>
<BODY>
<H1>Order Confirmation</H1>
Your order has been successfully placed! Thank you for shopping at Tekmart.com.
</BODY>
</HTML>
这个JSP只是简单地显示一个订单确认消息。
12. 整体流程总结
下面是整个Web应用的流程图:
graph LR
A[登录页面(index.html)] --> B[Catalog Servlet]
B --> C[catalog.jsp]
C --> D[Recommend Servlet]
D --> E[recommend.jsp]
E --> F[Purchase Servlet]
F --> G[purchase.jsp]
整个流程如下:
1. 用户访问登录页面,输入客户ID并提交表单。
2. Catalog Servlet处理登录请求,创建订单对象,查询产品列表,并将结果转发到
catalog.jsp
。
3. 用户在
catalog.jsp
中选择商品并提交表单。
4. Recommend Servlet处理商品选择请求,运行推荐逻辑,并将推荐结果转发到
recommend.jsp
。
5. 用户在
recommend.jsp
中选择推荐商品并提交表单。
6. Purchase Servlet处理最终订单,调用订单处理逻辑,并将结果转发到
purchase.jsp
。
7. 用户在
purchase.jsp
中看到订单确认消息。
13. 总结与展望
通过以上的实践,我们展示了如何使用Servlet和JSP构建一个完整的Web应用。在这个过程中,我们学习了如何使用JSP和Servlet协作,如何在不同组件之间共享信息,以及如何处理用户请求和业务逻辑。
在实际开发中,还可以对这个应用进行进一步的优化和扩展。例如,可以添加更多的业务规则,完善用户界面,提高系统的安全性和性能等。同时,合理使用自动化测试工具可以帮助我们更快地发现和解决问题,提高开发效率。
总之,Servlet和JSP是Java Web开发中非常重要的技术,掌握它们可以让我们构建出更加高效、可维护的Web应用。希望本文能对大家有所帮助,让大家在Java Web开发的道路上更进一步。
超级会员免费看

被折叠的 条评论
为什么被折叠?



