简介:本资源是一套面向初学者的Struts+SQL开发源代码,深入涵盖了Struts框架、JSP、Servlet和SQL技术。通过“StrutsDB”实例,学习者将了解如何将这些技术整合进实际项目中,包括MVC设计模式的应用、动态页面生成、请求处理以及数据库交互。
1. Struts框架介绍与应用
1.1 Struts框架概述
Struts框架是Apache软件基金会的一个开源项目,属于Java EE的MVC框架。自2000年推出以来,它一直是企业级Java Web开发的首选工具之一。Struts旨在简化MVC模式的实现,并通过提供丰富的标签库、表单验证、国际化支持等,来加速开发周期。
1.2 Struts的工作原理
Struts的核心思想是通过定义一个中心控制器Servlet来管理应用程序的流程。开发者通过配置文件(struts.xml)来定义动作(Action)和表单(Form),Struts会自动处理请求到动作的映射,以及动作到视图的转发。
<struts>
<package name="default" extends="struts-default">
<action name="login" class="com.example.LoginAction">
<result name="success">/loginSuccess.jsp</result>
<result name="input">/login.jsp</result>
</action>
</package>
</struts>
1.3 Struts的应用场景
Struts框架特别适合于处理复杂的Web应用程序,尤其是那些需要大量表单处理和数据验证的应用。由于它的扩展性极好,开发者可以方便地引入自定义拦截器、验证器和其他组件来满足特定需求。
在下一章节中,我们将深入探讨JSP动态内容生成的方式,了解如何与JavaBean集成以及如何开发自定义标签。这将为构建交互式Web应用打下坚实的基础。
2. JSP动态内容生成
2.1 JSP的基本概念与结构
2.1.1 JSP页面的组成与生命周期
JSP(Java Server Pages)是一种动态网页技术,允许开发者将Java代码嵌入到HTML页面中。JSP页面通常以 .jsp
作为文件扩展名。与普通的HTML页面不同,JSP 页面能够处理服务器端逻辑,并动态生成 HTML 内容响应用户的请求。
一个典型的 JSP 页面生命周期可以分为以下几个阶段:
-
翻译阶段 :当第一次访问 JSP 页面时,容器会将 JSP 页面翻译成一个 Java Servlet 类。这个翻译过程包括了页面中嵌入的 Java 代码,JSP 元素(指令、脚本、动作等)和 HTML 标记的处理。
-
加载和实例化阶段 :容器加载翻译后生成的 Servlet 类,并创建该类的一个实例。
-
初始化阶段 :容器调用初始化方法(
jspInit
),该方法通常用于执行一些只需要执行一次的初始化操作。 -
处理请求阶段 :每当有 HTTP 请求到达时,容器会调用
jspService
方法来处理请求。这个方法包含了处理请求和生成响应的逻辑。 -
销毁阶段 :当容器决定移除 JSP 页面时,会调用
jspDestroy
方法,该方法用于执行一些清理工作,如关闭数据库连接等。
2.1.2 JSP指令、动作和脚本元素
JSP 页面由不同的元素组成,这些元素可以大致分为三类:指令、动作和脚本元素。
- 指令(Directives) :用于告诉 JSP 容器如何处理整个页面或者一部分页面。常见的指令包括
page
、include
和taglib
。jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ include file="copyright.jsp" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
上述示例中,第一个 page
指令设置了页面的类型和字符编码, include
指令嵌入了版权信息页面, taglib
指令引入了 JSTL 核心标签库。
- 动作(Actions) :提供了一种更简洁的方式来处理一些常见的任务,如使用
jsp:forward
前转请求,使用jsp:param
向标签处理程序传递参数等。
jsp <jsp:forward page="success.jsp"> <jsp:param name="message" value="Welcome to JSP"/> </jsp:forward>
这里 jsp:forward
动作将请求转发到 success.jsp
页面,并携带了一个名为 message
的参数。
- 脚本元素(Scripting Elements) :允许开发者在 JSP 页面中嵌入 Java 代码。它们分为脚本声明、脚本表达式和脚本片段。
```jsp <%! String currentDate() { return new java.util.Date().toString(); } %>
The current date and time is <%= currentDate() %>.
```
在这段代码中, <%! %>
之间是脚本声明,用于定义方法, <%= %>
之间是脚本表达式,用于输出方法返回值。
2.2 JSP与JavaBean的集成
2.2.1 JavaBean的作用与创建
JavaBean 是遵循特定规范编写的 Java 类,这些类通常被设计为可重用的软件组件。它们通常是简单的 Java 类,并且满足以下条件:
- 必须具有一个公共的无参构造函数。
- 所有属性必须私有化,并通过公共的 getter 和 setter 方法进行访问。
- 它们能够序列化以便在网络上传输,或者用于会话状态管理。
创建一个 JavaBean 的基本步骤如下:
- 定义类并添加私有属性。
- 为每个私有属性添加公共的 getter 和 setter 方法。
- 可以添加业务逻辑方法。
public class User {
private String username;
private String password;
public User() {}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2.2.2 在JSP中使用JavaBean
在 JSP 页面中使用 JavaBean 可以简化代码和提高可维护性。使用 <jsp:useBean>
动作可以创建或者找到一个 JavaBean 实例,然后可以通过 <jsp:getProperty>
和 <jsp:setProperty>
来获取和设置属性。
<jsp:useBean id="user" class="com.example.User" scope="request"/>
<jsp:setProperty name="user" property="*" />
<p>Hello, <%= user.getUsername() %>!</p>
上述代码中, <jsp:useBean>
创建了 User
类的实例, scope
属性定义了 Bean 的作用域,可以是 page
、 request
、 session
或 application
。 <jsp:setProperty>
自动匹配请求参数到 JavaBean 属性,而 <jsp:getProperty>
用于输出属性值。
2.3 JSP自定义标签开发
2.3.1 标签库描述符(TLD)的编写
JSP 自定义标签通过标签库描述符(TLD)文件来定义。TLD 文件是一个 XML 文件,它告诉容器有关自定义标签的信息,包括标签的名称、处理器类以及标签体是否可以包含内容等。通常 TLD 文件放在 WEB-INF 目录下的某个文件夹中。
<%@ taglib prefix="my" uri="/WEB-INF/mytags.tld" %>
<my:helloWorld/>
上面的代码片段中, my
是一个前缀,用来区分自定义标签和其他标签库, helloWorld
是自定义标签的名称。
一个基本的 TLD 文件如下:
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>My Tags</short-name>
<uri>http://www.example.com/mytags</uri>
<tag>
<name>helloWorld</name>
<tag-class>com.example.tags.HelloWorldTag</tag-class>
<body-content>empty</body-content>
<display-name>Hello World Tag</display-name>
<small-icon></small-icon>
<large-icon></large-icon>
</tag>
</taglib>
2.3.2 标签处理器(Tag Handler)的实现
标签处理器是自定义标签的核心,它实现了自定义标签的行为。要创建一个标签处理器,需要继承 SimpleTagSupport
类并重写 doTag
方法。
public class HelloWorldTag extends SimpleTagSupport {
public void doTag() throws JspException, IOException {
getJspContext().getOut().write("Hello, World!");
}
}
上述代码定义了一个简单的标签处理器 HelloWorldTag
,当标签被执行时,它会输出 "Hello, World!" 到页面上。
在本章节中,我们详细介绍了 JSP 页面的基本概念、结构组成和生命周期,解释了 JSP 指令、动作和脚本元素的使用方法,并且演示了如何将 JSP 与 JavaBean 集成以及如何开发自定义标签。这些知识为后续实现复杂的 Web 应用提供了坚实的基础。在下一章节中,我们将深入探讨 Servlet 技术,它是 Java Web 开发的核心组件之一,能够处理 HTTP 请求并生成响应。
3. Servlet处理HTTP请求
3.1 Servlet基础与生命周期
3.1.1 Servlet接口与Servlet容器
Servlet是运行在服务器端的小型Java程序,它遵循特定的接口和生命周期规范,用于处理客户端(通常是Web浏览器)的请求,并返回响应。Java Servlet API提供了Servlet接口,所有Servlet类都需要实现这个接口。当客户端发出请求时,Servlet容器(如Apache Tomcat)负责加载Servlet类,创建Servlet实例,并调用其方法来处理请求。
Servlet容器是Web服务器的一部分,它提供运行Servlet的环境,并管理Servlet的生命周期。容器管理的Servlet生命周期涉及三个主要方法:init(), service(), 和destroy()。
-
init(ServletConfig config)
: 在Servlet实例化后和第一次请求之前调用。此方法用于初始化Servlet。 -
service(ServletRequest req, ServletResponse res)
: 负责处理客户端的请求,并产生响应。每次请求Servlet时,容器都会调用此方法。 -
destroy()
: 在Servlet被卸载或Web应用停止前调用,用于执行一些清理工作。
3.1.2 Servlet的生命周期方法
在Servlet的生命周期中,这三个方法都扮演着重要的角色:
-
init()
: 这是Servlet启动的标志。在这个方法中,你可以进行初始化操作,例如读取配置文件、初始化数据库连接等。该方法只会被调用一次,所以应尽量避免在此方法中执行耗时或重量级的操作。 -
service()
: 这是Servlet处理请求的主要方法。当请求到达时,Servlet容器会创建一个新的线程来调用此方法。因为线程是共享的资源,所以要确保在service()方法中的代码是线程安全的。 -
destroy()
: 在Servlet被销毁前调用,可以在这里进行资源的释放操作,例如关闭数据库连接。
代码示例:
public class MyServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
// Servlet初始化代码
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求
}
@Override
public void destroy() {
// 清理资源代码
}
}
在service方法中,实际上由容器调用doGet, doPost等方法来处理具体的HTTP请求类型。例如,对于GET请求,容器会调用 doGet(HttpServletRequest req, HttpServletResponse resp)
方法。
3.1.2 Servlet的生命周期方法分析
-
init()
: 在这里初始化一些只做一次的设置。例如,读取配置信息,建立数据库连接,或者初始化资源。init()
方法接收一个ServletConfig
对象,该对象包含Servlet配置信息,如初始化参数。 -
service()
: 所有对Servlet的请求都会通过service方法转发到对应的处理方法(如doGet
,doPost
)。这个方法检查请求的类型,并调用适当的方法处理请求。例如,HTTP GET请求会调用doGet()
方法。 -
destroy()
: 当Web应用卸载或Servlet容器关闭时,会调用此方法。这是释放资源的好地方,比如关闭打开的文件,数据库连接等。
这些生命周期方法都是容器管理的,开发者通常不需要直接调用它们。了解这些方法能够帮助开发者更好地理解Servlet在Web应用中的运作方式。
3.2 Servlet与HTTP协议
3.2.1 HTTP请求与响应的处理
Servlet通过其service方法接收和处理HTTP请求。HTTP请求由请求行、请求头、空行和请求体组成。在service方法中,容器将解析这些信息,并将其包装成 HttpServletRequest
和 HttpServletResponse
对象。开发者可以使用这些对象来处理请求和生成响应。
HttpServletRequest
对象提供了获取客户端请求信息的方法,如获取参数、获取请求头、处理cookie等。 HttpServletResponse
对象用于生成响应,开发者可以设置响应的内容类型,写入响应体等。
示例代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应内容类型
response.setContentType("text/html");
// 实际的逻辑是在这里编写
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, world!</h1>");
out.println("</body></html>");
}
在上述例子中, doGet
方法通过 response.getWriter()
获取响应的 PrintWriter
对象,并写入一个简单的HTML响应。
3.2.2 Servlet过滤器与监听器的应用
Servlet过滤器(Filter)和监听器(Listener)是Servlet API提供的两种扩展点,它们用于处理请求、响应和Servlet的生命周期事件。
过滤器可以对请求和响应进行预处理或后处理。典型的用途包括:日志记录、验证、图像转换等。过滤器需要实现 javax.servlet.Filter
接口。
监听器可以监听在Servlet生命周期中发生的各种事件,如对象创建、销毁、属性更改通知等。典型的用途包括:会话跟踪、计数器等。监听器需要实现 javax.servlet.ServletContextListener
或 javax.servlet.http.HttpSessionListener
等接口。
示例代码(过滤器):
public class LogFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
// 过滤器初始化
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 在请求到达Servlet之前执行的操作
chain.doFilter(request, response); // 继续传递请求到下一个过滤器或Servlet
// 在响应从Servlet返回客户端之后执行的操作
}
public void destroy() {
// 过滤器销毁
}
}
在Servlet 3.0及以后,过滤器和监听器也可以通过注解来声明。
3.3 Servlet的高级特性
3.3.1 会话跟踪与管理
HTTP是一种无状态的协议,意味着服务器不会保存任何关于客户端请求的信息。然而,Web应用通常需要能够识别用户的状态,例如用户登录后的购物车操作。这便是会话跟踪的作用。
Servlet提供了 HttpSession
接口来管理用户会话。每个用户都会有一个唯一的会话标识符,通常通过Cookie或URL重写传递。开发者可以在会话中存储用户信息,如用户偏好、购物车内容等。
示例代码:
// 获取会话对象
HttpSession session = request.getSession();
// 设置属性
session.setAttribute("cart", new ShoppingCart());
// 获取属性
ShoppingCart cart = (ShoppingCart) session.getAttribute("cart");
HttpSession
接口的常用方法包括 setAttribute
, getAttribute
, invalidate
等,用于管理会话中的数据。
3.3.2 多线程安全与性能优化
由于Servlet是多线程的,因此在处理业务逻辑时必须考虑线程安全问题。避免使用共享的实例变量来存储状态信息,除非它们是同步的。
关于性能优化,可以通过合理配置Servlet缓存,使用异步处理,和减少资源加载时间来提高响应速度。同时,也可以通过代码的重构和优化来减少不必要的计算。
示例代码(同步方法):
public synchronized String getUserSessionId(HttpServletRequest request) {
String sessionId = request.getParameter("sessionId");
// 使用sessionId做一些业务逻辑
return sessionId;
}
在上述代码中,通过添加 synchronized
关键字,确保了 getUserSessionId
方法在多线程环境下是线程安全的。
综上所述,通过理解并应用Servlet生命周期方法、HTTP请求处理、过滤器和监听器、会话管理以及线程安全和性能优化等高级特性,开发者可以构建出稳定、高效、安全的Web应用。
4. SQL数据库操作
4.1 SQL语言基础
SQL(Structured Query Language)是用于存储、检索和操作数据库的标准编程语言。在本节中,我们将探讨SQL语言的基础,包括数据定义语言(DDL)和数据操纵语言(DML)以及数据查询语言(DQL)。
4.1.1 数据定义语言(DDL)
DDL是用于定义或修改数据库结构的SQL命令。主要的DDL命令包括:
-
CREATE
: 用于创建数据库中的对象,如表、索引或视图。 -
ALTER
: 用于修改已存在的数据库对象。 -
DROP
: 用于删除数据库中的对象。
下面的代码块展示了如何使用DDL创建一个简单的用户表:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
在这里, CREATE TABLE
是用来创建一个新表的DDL命令。 id
字段被设置为自动增长的主键, username
和 password
是不允许为空的字符串字段, email
是一个可选的字符串字段, created_at
是一个时间戳字段,它默认设置为当前时间。
4.1.2 数据操纵语言(DML)与数据查询语言(DQL)
DML提供了添加、修改、删除记录的能力,而DQL允许用户查询存储在数据库中的数据。主要的DML和DQL命令包括:
-
INSERT
: 用于向表中插入新的数据行。 -
UPDATE
: 用于更新表中的现有数据。 -
DELETE
: 用于从表中删除数据行。 -
SELECT
: 用于从数据库中选择数据。
一个简单的 INSERT
语句示例如下:
INSERT INTO users (username, password, email)
VALUES ('newuser', 'password123', 'newuser@example.com');
此语句向 users
表中插入了一个新的用户记录。
一个 SELECT
查询的例子则可能如下:
SELECT * FROM users WHERE username = 'newuser';
这个查询语句会选择 users
表中所有字段中用户名为 newuser
的记录。
DML和DQL的使用允许开发者以编程方式处理数据,这在任何涉及数据库的应用程序中都是至关重要的。
4.2 高级SQL查询技巧
4.2.1 联合查询与子查询
SQL查询可以变得相当复杂,涉及多个表的联合查询或使用子查询。这些高级技巧可以在复杂的数据库操作中发挥重要作用。
联合查询 利用 JOIN
语句合并来自两个或多个表的数据。常见的联合类型包括内连接(INNER JOIN)、左连接(LEFT JOIN)、右连接(RIGHT JOIN)和全外连接(FULL OUTER JOIN)。
例如,将用户表和另一个表(如订单表)进行内连接,可以这样写:
SELECT u.*, o.order_id
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
这段代码将用户表与订单表进行内连接,并选择所有匹配用户和订单信息的字段。
子查询 是一种嵌套在其他SQL语句中的查询。它在 WHERE
子句或 FROM
子句中使用,用于实现复杂的查询逻辑。
例如,使用子查询来查找订单数量超过特定值的用户,可以使用如下代码:
SELECT username
FROM users
WHERE id IN (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING COUNT(order_id) > 10
);
这里,内部的 SELECT
子查询首先计算每个用户的订单数量,并使用 HAVING
子句过滤出订单数量超过10的用户。外部的 SELECT
然后返回这些用户的用户名。
4.2.2 视图与存储过程
视图 是基于SQL语句的结果集的可视化表示。它是一个虚拟表,其内容由查询定义。视图可以用来简化复杂的SQL操作,提高安全性以及限制对数据的访问。
创建一个视图的基本语法如下:
CREATE VIEW user_orders AS
SELECT u.username, o.order_id
FROM users u
JOIN orders o ON u.id = o.user_id;
在这个例子中,我们创建了一个名为 user_orders
的视图,它显示了用户的用户名和他们的订单ID。
存储过程 是一组为了完成特定功能的SQL语句集,它被编译并存储在数据库中。存储过程可以包含复杂的逻辑,包括条件语句、循环、临时变量等。
创建一个简单的存储过程的例子:
DELIMITER //
CREATE PROCEDURE GetUserOrders(IN user_id_param INT)
BEGIN
SELECT o.order_id, o.order_date
FROM orders o
WHERE o.user_id = user_id_param;
END //
DELIMITER ;
这个存储过程 GetUserOrders
接收一个用户ID作为参数,并返回该用户的所有订单信息。使用 DELIMITER
是因为存储过程的定义可能包含分号( ;
),这是默认的语句分隔符。
4.3 SQL性能优化
4.3.1 索引的创建与优化
索引在数据库中扮演着非常重要的角色,它们可以显著提高查询的速度。但是,索引也需要维护,并且会占用额外的存储空间。因此,合理地创建和管理索引是优化SQL性能的关键。
创建索引的一般规则是:
- 为经常用于搜索、连接和排序的列创建索引。
- 考虑不为列的前缀或具有高重复值的列创建索引。
例如,为 users
表的 username
字段创建索引:
CREATE INDEX idx_username ON users(username);
在创建索引之后,需要定期监控和评估索引的效率,并根据实际查询需求做出相应调整。
4.3.2 SQL执行计划分析与优化
SQL执行计划是数据库为执行特定SQL语句所采取的步骤的详细描述。理解执行计划对于性能优化至关重要。
大多数数据库系统提供了解释执行计划的工具。在MySQL中,可以使用 EXPLAIN
关键字来查看查询的执行计划。例如:
EXPLAIN SELECT * FROM users WHERE username = 'newuser';
这将返回一个结果集,它描述了数据库是如何执行这个 SELECT
查询的,包括访问的表类型、可能用到的索引、参与连接的表等信息。
通过分析执行计划,可以识别查询中的性能瓶颈。对于发现的问题,可能需要重构查询,增加或修改索引,甚至调整数据库的配置参数,以优化性能。
总结: SQL语言基础覆盖了DDL和DML/DQL的基本用法。高级查询技巧包括联合查询、子查询、视图和存储过程,这些可以显著增加查询的能力和灵活性。性能优化则涉及索引的创建和管理以及SQL执行计划的分析,都是提升数据库操作效率的重要手段。在本章节中,我们深入探讨了SQL语言的各个方面,为数据库开发者提供必要的知识和工具,以构建高效和优化的数据库应用。
5. MVC架构的实际应用
5.1 MVC模式的核心理念
5.1.1 模型(Model)、视图(View)与控制器(Controller)
在Web开发中,MVC(Model-View-Controller)架构是一种将应用分割为三部分的模式,以实现更好的解耦和代码重用。模型(Model)是应用的业务逻辑部分,它处理数据、业务规则和逻辑。视图(View)是用户界面,即用户在屏幕上看到并与之交互的页面。控制器(Controller)则是处理用户输入的部分,它把用户的请求发送给模型处理,并选择视图来显示处理的结果。
5.1.2 MVC的优势与应用场景
MVC模式之所以广受欢迎,是因为它有以下几个优势:
- 松耦合 :各组件之间的依赖性降低,便于维护和修改。
- 高内聚 :每个组件负责特定功能,逻辑清晰。
- 可扩展性 :新的模型、视图和控制器可以轻松添加到现有系统中。
MVC适合于复杂和可扩展的应用程序,特别是在需要分离业务逻辑和表示逻辑的场景中。比如电子商务网站、内容管理系统和多层架构的企业级应用程序。
5.2 Struts与MVC的结合
5.2.1 Struts框架中的MVC实现
Struts是一个基于Java的MVC框架,它提供了一套丰富的标签库来简化JSP页面的编写,同时利用Action和ActionForm等组件实现了MVC中的C(控制器)和M(模型)部分。在Struts中,控制器由ActionServlet实现,它负责处理所有的请求并根据请求调用相应的Action,然后根据Action的执行结果选择合适的视图(JSP页面)来显示。
5.2.2 表单验证与消息资源处理
在Web应用中,用户输入的数据验证非常重要。Struts框架通过ActionForm类来处理表单数据,并提供了一个内置的验证框架,可以自动验证用户输入的数据。同时,Struts还支持国际化和本地化,使得开发者可以根据不同区域显示不同语言的消息资源。
5.3 Web开发技能提升
5.3.1 前端技术与后端框架的整合
随着Web开发技术的发展,前端技术与后端框架的整合变得越来越重要。前端框架(如React、Angular、Vue.js)可以提供更加动态和响应式的用户体验。后端框架(如Struts、Spring、Hibernate)则提供强大的数据处理能力。开发者需要掌握如何将这些前端技术与后端框架结合起来,实现一个完整的、功能强大的Web应用。
5.3.2 开发环境与工具的搭建
开发环境和工具的搭建对提高开发效率至关重要。现代的Web开发工具链包括代码编辑器(如IntelliJ IDEA、VS Code)、构建工具(如Maven、Gradle)、版本控制(如Git)和持续集成/持续部署(CI/CD)工具(如Jenkins)。掌握这些工具的配置和使用,可以有效提升Web应用的开发和部署效率。
简介:本资源是一套面向初学者的Struts+SQL开发源代码,深入涵盖了Struts框架、JSP、Servlet和SQL技术。通过“StrutsDB”实例,学习者将了解如何将这些技术整合进实际项目中,包括MVC设计模式的应用、动态页面生成、请求处理以及数据库交互。