Java Web开发实战教材源码详解
简介:《Java Web开发实战教材源码》是一份涵盖Java Web开发全流程的学习资源,共包含15个章节,每个章节围绕一个主题展开,帮助学习者深入掌握Java在Web开发中的核心应用。内容涵盖Java基础、Servlet与JSP、MVC架构、JDBC数据库连接、JSTL与EL表达式、过滤器与监听器、会话管理、Ajax与jQuery交互、Web部署与容器、以及Web安全防护等关键技术。通过该源码的实战学习,开发者可以全面掌握Java Web开发技能,提升实际项目开发能力,适用于初学者和进阶者的学习与提升。 
1. Java Web开发概述
Java Web开发是构建企业级Web应用的重要技术栈,其依托Java语言的跨平台性和稳定性,在大型系统中占据核心地位。本章将从Java Web的基本概念入手,解析其整体架构体系,包括Servlet、JSP、MVC模式等核心组件的协同工作机制。同时,我们还将介绍主流开发框架如Spring、Struts、Hibernate在企业应用中的角色与价值,帮助读者建立系统性的认知基础。通过本章学习,读者将明确本书的学习目标,并理解源码实践在掌握Java Web核心技术中的关键作用。
2. Java基础语法与面向对象编程
Java Web开发离不开扎实的Java语言基础,本章从语法规范到面向对象编程思想,全面解析Java语言的核心机制,并结合实际开发场景进行示例说明。
2.1 Java基础语法要点
在Java Web开发中,基础语法是构建整个应用程序的基石。掌握数据类型、变量定义、运算符、表达式以及控制流程语句,是编写高效、可维护代码的前提。
2.1.1 数据类型与变量定义
Java是一种静态类型语言,所有变量在使用前必须先声明其类型。Java中的数据类型分为基本数据类型和引用数据类型两大类。
Java基本数据类型
| 类型 | 大小(字节) | 范围/描述 |
|---|---|---|
| byte | 1 | -128 到 127 |
| short | 2 | -32768 到 32767 |
| int | 4 | -2^31 到 2^31-1 |
| long | 8 | -2^63 到 2^63-1(后缀L) |
| float | 4 | 单精度浮点数(后缀F) |
| double | 8 | 双精度浮点数 |
| char | 2 | Unicode字符(0 到 65535) |
| boolean | 1 | true 或 false |
示例代码:
public class DataTypeExample {
public static void main(String[] args) {
byte b = 100;
short s = 10000;
int i = 100000;
long l = 1000000000L;
float f = 3.14f;
double d = 3.1415926535;
char c = 'A';
boolean flag = true;
System.out.println("byte: " + b);
System.out.println("short: " + s);
System.out.println("int: " + i);
System.out.println("long: " + l);
System.out.println("float: " + f);
System.out.println("double: " + d);
System.out.println("char: " + c);
System.out.println("boolean: " + flag);
}
}
代码分析:
byte、short、int、long:整型变量,用于存储整数。注意long类型的值需要加后缀L。float与double:浮点型变量,用于表示小数,float需要加后缀f。char:字符类型,用单引号表示。boolean:布尔类型,只有true和false两个值。- 所有变量在使用前必须先声明并赋值,否则编译器会报错。
2.1.2 运算符与表达式
Java支持丰富的运算符,包括算术运算符、赋值运算符、关系运算符、逻辑运算符等。
常用运算符分类
| 类型 | 运算符示例 |
|---|---|
| 算术运算符 | +, -, *, /, % |
| 赋值运算符 | =, +=, -=, *=, /= |
| 关系运算符 | ==, !=, >, <, >=, <= |
| 逻辑运算符 | &&, |
| 条件运算符 | ? : |
示例代码:
public class OperatorExample {
public static void main(String[] args) {
int a = 10, b = 20;
int result;
// 算术运算
result = a + b;
System.out.println("a + b = " + result);
// 赋值运算
result += a;
System.out.println("result += a -> " + result);
// 关系运算
System.out.println("a > b ? " + (a > b));
// 逻辑运算
System.out.println("a < b && b > 15 ? " + (a < b && b > 15));
// 条件运算
int max = (a > b) ? a : b;
System.out.println("最大值为:" + max);
}
}
代码分析:
+、-、*、/、%:基本的数学运算符,注意除法运算中整数相除会舍弃小数部分。+=、-=:复合赋值运算符,可以简化代码。==、!=、>、<:用于比较两个值的关系。&&、||、!:逻辑运算符,用于构建复杂的判断条件。? ::三元运算符,常用于简化if-else结构。
2.1.3 控制流程语句(if、for、while等)
控制流程语句决定了程序的执行顺序。Java提供了 if-else 、 switch-case 、 for 、 while 、 do-while 等结构。
示例代码:
public class ControlFlowExample {
public static void main(String[] args) {
int score = 85;
// if-else结构
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else {
System.out.println("及格");
}
// for循环
System.out.println("for循环输出1-5:");
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
// while循环
System.out.println("\nwhile循环输出1-5:");
int j = 1;
while (j <= 5) {
System.out.print(j + " ");
j++;
}
// switch-case
int day = 3;
System.out.println("\nswitch-case判断星期:");
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
default:
System.out.println("其他");
}
}
}
代码分析:
if-else:用于条件判断,根据条件执行不同代码块。for:适用于已知循环次数的场景。while:当条件为真时持续执行循环体。switch-case:用于多条件分支判断,注意每个case后要加break,否则会继续执行下一个case。
2.2 面向对象编程思想
面向对象编程(OOP)是Java语言的核心特性,通过类与对象来组织代码,提升程序的可维护性和复用性。
2.2.1 类与对象的定义和使用
类是对象的模板,对象是类的具体实例。通过类定义对象的属性和行为。
示例代码:
// 定义一个类
class Person {
String name;
int age;
void sayHello() {
System.out.println("Hello, my name is " + name + ", I am " + age + " years old.");
}
}
public class OOPExample {
public static void main(String[] args) {
// 创建对象
Person person1 = new Person();
person1.name = "Alice";
person1.age = 25;
Person person2 = new Person();
person2.name = "Bob";
person2.age = 30;
// 调用方法
person1.sayHello();
person2.sayHello();
}
}
代码分析:
Person类定义了两个属性:name和age,以及一个方法sayHello()。- 使用
new Person()创建类的实例对象person1和person2。 - 每个对象都有自己的属性值,互不干扰。
sayHello()方法展示了如何通过对象调用类的方法。
2.2.2 封装、继承与多态特性
面向对象的三大核心特性是封装、继承与多态。
封装
封装是指将对象的状态和行为绑定在一起,并对外隐藏实现细节。
class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
继承
继承是指子类继承父类的属性和方法,实现代码复用。
class Animal {
void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
void bark() {
System.out.println("Dog is barking");
}
}
多态
多态是指一个接口可以有多个不同的实现。
class Shape {
void draw() {
System.out.println("Drawing Shape");
}
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing Circle");
}
}
class Square extends Shape {
void draw() {
System.out.println("Drawing Square");
}
}
示例执行:
public class PolymorphismExample {
public static void main(String[] args) {
Shape s1 = new Circle();
Shape s2 = new Square();
s1.draw(); // 输出 "Drawing Circle"
s2.draw(); // 输出 "Drawing Square"
}
}
代码分析:
- 封装 :通过
private修饰符隐藏属性,通过getter/setter方法控制访问。 - 继承 :
Dog类继承了Animal类的方法,可扩展新的行为。 - 多态 :
Shape类的引用可以指向其子类实例,运行时调用实际对象的方法。
2.2.3 接口与抽象类的区别及应用
接口与抽象类都是实现抽象化的重要机制,但它们有本质区别。
抽象类与接口的对比
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 方法实现 | 可包含具体方法实现 | 默认方法为抽象,Java 8+可有默认实现 |
| 构造函数 | 有 | 无 |
| 成员变量 | 可为任意访问权限 | 默认public static final |
| 继承方式 | 单继承 | 多实现 |
示例代码:
// 抽象类
abstract class Animal {
abstract void makeSound();
void sleep() {
System.out.println("Animal is sleeping");
}
}
// 接口
interface Flyable {
void fly();
}
// 实现
class Bird extends Animal implements Flyable {
void makeSound() {
System.out.println("Bird is singing");
}
public void fly() {
System.out.println("Bird is flying");
}
}
public class InterfaceAbstractExample {
public static void main(String[] args) {
Bird bird = new Bird();
bird.makeSound();
bird.sleep();
bird.fly();
}
}
代码分析:
Animal是一个抽象类,包含抽象方法makeSound()和具体方法sleep()。Flyable是一个接口,定义了fly()方法。Bird类同时继承抽象类并实现接口,展示了Java中类的多特性继承能力。
2.3 Java异常处理机制
异常处理是Java程序健壮性的重要保障。Java提供了try-catch-finally结构以及自定义异常机制。
2.3.1 异常分类与捕获处理
Java中的异常分为 受检异常(Checked Exception) 和 非受检异常(Unchecked Exception) 。
异常分类图(mermaid流程图):
graph TD
A[Throwable] --> B[Error]
A --> C[Exception]
C --> D[IOException]
C --> E[SQLException]
C --> F[RuntimeException]
F --> G[ArithmeticException]
F --> H[NullPointerException]
示例代码:
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // 触发ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常:" + e.getMessage());
} finally {
System.out.println("无论是否异常,都会执行的代码");
}
}
}
代码分析:
try:用于包裹可能抛出异常的代码。catch:捕获并处理特定类型的异常。finally:无论是否发生异常,都会执行的代码块,常用于资源释放。- 上例中访问数组越界时抛出
ArrayIndexOutOfBoundsException,被catch捕获处理。
2.3.2 自定义异常类的设计与实现
Java允许开发者自定义异常类,继承自 Exception 或其子类。
示例代码:
// 自定义异常类
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
// 使用自定义异常
public class CustomExceptionExample {
static void validateAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("年龄不合法");
}
System.out.println("年龄合法:" + age);
}
public static void main(String[] args) {
try {
validateAge(-5);
} catch (InvalidAgeException e) {
System.out.println("捕获自定义异常:" + e.getMessage());
}
}
}
代码分析:
InvalidAgeException继承自Exception,构造函数接受异常信息。validateAge()方法在年龄不合法时抛出该异常。main()方法中使用try-catch捕获并处理自定义异常。
2.4 Java集合框架概述
Java集合框架提供了丰富的数据结构和算法实现,是Java开发中不可或缺的一部分。
2.4.1 List、Set、Map的常用实现类
| 接口 | 常用实现类 | 特点说明 |
|---|---|---|
| List | ArrayList, LinkedList | 有序、可重复 |
| Set | HashSet, TreeSet | 无序、不可重复,HashSet无序,TreeSet有序 |
| Map | HashMap, TreeMap | 键值对结构,HashMap无序,TreeMap按键排序 |
示例代码:
import java.util.*;
public class CollectionExample {
public static void main(String[] args) {
// List
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
System.out.println("List: " + list);
// Set
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // 重复元素不会添加
System.out.println("Set: " + set);
// Map
Map<String, Integer> map = new HashMap<>();
map.put("Math", 90);
map.put("English", 85);
System.out.println("Map: " + map);
}
}
代码分析:
ArrayList:基于数组实现,查询快、增删慢。HashSet:基于哈希表实现,不保存重复元素,无序。HashMap:键值对结构,允许null键和值。
2.4.2 集合操作的性能优化与使用技巧
集合的性能优化主要包括:
- 选择合适的数据结构 :如频繁查询用
ArrayList,频繁增删用LinkedList。 - 初始容量设置 :避免频繁扩容,提升性能。
- 使用迭代器 :避免在遍历中修改集合引发并发修改异常。
示例代码:
import java.util.*;
public class CollectionOptimization {
public static void main(String[] args) {
// 设置初始容量
List<Integer> list = new ArrayList<>(1000);
// 使用迭代器删除
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
if (name.equals("Bob")) {
iterator.remove(); // 安全删除
}
}
System.out.println(names);
}
}
代码分析:
- 使用
ArrayList<>(1000)初始化容量,减少扩容次数。 - 在遍历集合时使用
Iterator.remove()安全删除元素,避免ConcurrentModificationException异常。
3. Servlet原理与请求处理
Servlet 是 Java Web 开发的核心组件之一,其作为服务器端程序的运行基础,负责接收和处理客户端的 HTTP 请求。本章将从 Servlet 的基础概念讲起,逐步深入到其生命周期、请求处理机制、上下文管理以及配置方式,帮助开发者构建对 Java Web 请求处理流程的完整理解。
3.1 Servlet基础与生命周期
3.1.1 Servlet的定义与执行流程
Servlet 是运行在 Web 服务器或应用服务器上的 Java 程序,用于扩展服务器的功能。其主要作用是接收来自客户端的请求(通常是 HTTP 请求),并生成响应返回给客户端。
Servlet 的执行流程如下:
- 客户端发送请求 :浏览器通过 HTTP 协议向服务器发起请求。
- 服务器接收请求 :服务器将请求交给对应的 Servlet 容器处理。
- 加载或创建 Servlet 实例 :如果该 Servlet 尚未被加载,则容器加载类并创建实例。
- 调用 init() 方法 :容器调用
init()方法初始化 Servlet,仅执行一次。 - 调用 service() 方法 :容器调用
service()方法处理请求。该方法会根据请求类型(GET、POST 等)调用相应的doGet()、doPost()方法。 - 生成响应 :Servlet 处理完成后,通过
HttpServletResponse对象将响应返回给客户端。 - 销毁 Servlet :当服务器关闭或重新部署时,容器调用
destroy()方法释放资源。
3.1.2 Servlet接口与GenericServlet类
Servlet 接口是所有 Servlet 的顶层接口,它定义了以下五个核心方法:
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
init():初始化方法,容器调用一次,用于配置初始化参数。service():处理请求的核心方法。destroy():在 Servlet 被销毁前调用,用于释放资源。getServletConfig():获取配置信息。getServletInfo():返回 Servlet 的描述信息。
为了简化开发,Java 提供了 GenericServlet 抽象类,实现了 Servlet 接口中的部分方法,如 init() 、 destroy() 、 getServletConfig() 、 getServletInfo() ,开发者只需继承该类并重写 service() 方法即可。
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
// 实现了 Servlet 接口的基本方法
}
3.1.3 HttpServlet的请求处理机制
HttpServlet 类继承自 GenericServlet ,并专门用于处理 HTTP 协议的请求。它根据 HTTP 方法(GET、POST、PUT、DELETE 等)提供了对应的 doGet() 、 doPost() 等方法。
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello, this is a GET response.");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello, this is a POST response.");
}
}
代码分析:
doGet():处理 GET 请求,客户端通过 URL 传递参数,适合获取数据。doPost():处理 POST 请求,参数通过请求体传递,适合提交数据。HttpServletRequest:封装了客户端请求的所有信息,包括请求头、请求参数、会话等。HttpServletResponse:用于向客户端发送响应,包括响应头、响应体、状态码等。
mermaid 流程图:Servlet 请求处理流程
graph TD
A[客户端发送请求] --> B[服务器接收请求]
B --> C[Servlet 容器加载/创建 Servlet 实例]
C --> D[调用 init()]
D --> E[调用 service()]
E --> F{请求类型}
F -->|GET| G[调用 doGet()]
F -->|POST| H[调用 doPost()]
G --> I[生成响应]
H --> I
I --> J[返回响应给客户端]
3.2 HTTP请求与响应的处理
3.2.1 GET与POST方法的区别与使用
GET 和 POST 是 HTTP 协议中最常用的两个请求方法,它们在用途、数据传递方式、安全性等方面存在显著差异。
| 特性 | GET 方法 | POST 方法 |
|---|---|---|
| 数据传递 | 通过 URL 的查询参数传递(Query String) | 通过请求体(Body)传递 |
| 缓存支持 | 支持 | 不支持 |
| 书签保存 | 可以 | 不可以 |
| 历史记录 | 保留在浏览器历史中 | 不保留在浏览器历史中 |
| 安全性 | 不安全,参数暴露在 URL 中 | 相对安全,参数不暴露在 URL 中 |
| 数据长度限制 | 有限(URL 长度限制) | 无限制 |
| 用途 | 获取数据 | 提交数据 |
3.2.2 请求参数的获取与处理
在 Servlet 中,可以通过 HttpServletRequest 对象获取请求参数。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h2>用户名:" + username + "</h2>");
out.println("<h2>密码:" + password + "</h2>");
out.println("</body></html>");
}
代码逻辑分析:
request.getParameter("username"):获取名为username的请求参数值。response.setContentType("text/html;charset=UTF-8"):设置响应内容类型为 HTML,并指定字符编码为 UTF-8。PrintWriter:用于向客户端输出 HTML 响应。
对于多个值的参数(如复选框),可以使用 getParameterValues() 方法:
String[] hobbies = request.getParameterValues("hobbies");
3.2.3 响应内容的生成与设置
Servlet 通过 HttpServletResponse 对象向客户端发送响应。常见的设置包括响应状态码、响应头、响应体等。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(HttpServletResponse.SC_OK); // 设置状态码为 200 OK
response.setHeader("Content-Type", "application/json"); // 设置响应头
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.print("{\"message\":\"请求成功\"}");
out.flush();
}
参数说明:
setStatus():设置 HTTP 状态码,如SC_OK(200)、SC_NOT_FOUND(404)等。setHeader():设置响应头,如 Content-Type、Cache-Control 等。setCharacterEncoding():设置字符编码,确保客户端能正确解析。
3.3 Servlet上下文与配置
3.3.1 ServletContext对象的作用
ServletContext 是一个全局对象,代表整个 Web 应用程序。它在整个 Web 应用生命周期中有效,可用于:
- 存储全局变量(通过
setAttribute()和getAttribute()) - 获取 Web 应用的初始化参数
- 获取资源路径(如文件路径)
public class MyContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
context.setAttribute("appVersion", "v1.0.0");
String version = (String) context.getAttribute("appVersion");
response.getWriter().write("当前应用版本:" + version);
}
}
代码逻辑分析:
getServletContext():获取当前 Web 应用的上下文对象。setAttribute():存储键值对,供其他 Servlet 或 JSP 页面访问。getAttribute():读取存储的属性值。
3.3.2 web.xml配置文件的编写
在传统的 Java Web 项目中, web.xml 文件用于配置 Servlet、过滤器、监听器等。
<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_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
<init-param>
<param-name>greeting</param-name>
<param-value>Hello World</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
参数说明:
<servlet>:定义一个 Servlet,包括名称和类路径。<init-param>:为 Servlet 配置初始化参数。<servlet-mapping>:将 Servlet 映射到特定的 URL 路径。
3.3.3 使用注解配置Servlet
从 Servlet 3.0 开始,可以使用注解简化配置。常见的注解包括 @WebServlet 、 @WebInitParam 等。
@WebServlet(urlPatterns = "/annotation", initParams = {
@WebInitParam(name = "config", value = "dev")
})
public class AnnotationServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String config = getServletConfig().getInitParameter("config");
response.getWriter().write("当前配置环境:" + config);
}
}
代码分析:
@WebServlet:声明一个 Servlet,指定 URL 映射。@WebInitParam:配置初始化参数。getServletConfig().getInitParameter():获取初始化参数值。
表格:XML 与 注解配置对比
| 配置方式 | 优点 | 缺点 |
|---|---|---|
| XML | 集中管理,适合大型项目 | 配置繁琐,容易出错 |
| 注解 | 简洁,代码与配置结合紧密 | 分散管理,不适合集中配置 |
通过本章的学习,读者应能够理解 Servlet 的运行机制、生命周期、HTTP 请求处理方式以及上下文和配置的使用。下一章将深入讲解 JSP 技术,为构建动态网页打下坚实基础。
4. JSP动态网页开发
Java Server Pages(JSP)是一种动态网页开发技术,允许开发者将Java代码嵌入HTML页面中,从而实现动态内容的生成。本章将从JSP的基本语法入手,逐步深入讲解其内置对象、与Servlet的协作方式,以及页面优化与安全控制策略。通过本章学习,读者将掌握JSP在现代Java Web开发中的核心应用。
4.1 JSP基础语法与指令
JSP技术的核心在于它能够在HTML页面中嵌入Java代码,从而实现动态内容的生成。JSP页面在第一次被请求时,会被编译成Servlet,之后的请求则直接使用已编译的Servlet来响应,这使得JSP具备良好的性能表现。
4.1.1 JSP页面结构与执行流程
JSP页面本质上是一个HTML文件,只不过其中可以包含Java代码片段。其结构主要包括以下几个部分:
- 静态内容 :如HTML标签、文本等。
- JSP脚本元素 :包括声明、表达式和脚本片段。
- JSP指令 :用于控制JSP引擎的行为。
- JSP动作标签 :用于调用内置对象或执行特定动作。
JSP页面的执行流程如下:
- 请求到达服务器 :客户端发起对JSP页面的请求。
- JSP解析与编译 :服务器将JSP页面翻译为Servlet源代码,并编译为字节码文件。
- Servlet执行 :编译后的Servlet被加载并执行,生成HTML响应。
- 响应返回客户端 :最终生成的HTML内容返回给客户端浏览器。
下面是一个简单的JSP页面示例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP示例</title>
</head>
<body>
<h1>欢迎访问JSP页面</h1>
<p>当前时间:<%= new java.util.Date() %></p>
</body>
</html>
代码逻辑分析 :
- <%@ page ... %> :这是page指令,设置页面的MIME类型、字符编码及使用的语言。
- <%= new java.util.Date() %> :这是一个JSP表达式,输出当前时间。
4.1.2 page、include、taglib指令详解
JSP指令用于控制页面的全局行为,常见的指令包括 page 、 include 和 taglib 。
page指令
page 指令用于定义页面的全局属性,常见属性如下:
| 属性名 | 作用说明 |
|---|---|
contentType |
设置响应内容类型及字符编码 |
language |
指定页面使用的脚本语言,默认为Java |
import |
导入Java类或包 |
session |
是否启用会话,默认为true |
示例:
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" session="false" %>
include指令
include 指令用于在JSP页面中静态包含其他资源(如HTML、JSP页面等)。
<%@ include file="header.jsp" %>
该指令在编译阶段就将被包含文件的内容插入当前页面中,因此称为 静态包含 。
taglib指令
taglib 指令用于引入自定义标签库(如JSTL)。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
此指令引入了JSTL核心标签库,并使用 c: 作为前缀。
4.1.3 JSP动作标签的使用
JSP动作标签是使用XML语法形式的标签,用于控制JSP运行时的行为。常见的动作标签包括:
| 标签 | 功能说明 |
|---|---|
<jsp:include> |
动态包含资源,运行时才处理 |
<jsp:forward> |
请求转发,将当前请求转发给其他页面 |
<jsp:useBean> |
创建或查找JavaBean对象 |
<jsp:setProperty> |
设置JavaBean的属性值 |
<jsp:getProperty> |
获取JavaBean的属性值 |
示例 :
<jsp:forward page="home.jsp" />
该标签将请求转发到 home.jsp 页面,用户浏览器地址栏不会发生变化。
4.2 JSP内置对象与作用域
JSP内置对象是JSP引擎自动创建的对象,开发者可以直接使用,无需显式声明。常见的内置对象包括 request 、 response 、 session 、 application 、 pageContext 等。
4.2.1 request、response、session对象的使用
- request对象 :封装客户端的HTTP请求信息,常用于获取请求参数、请求头等。
- response对象 :用于向客户端发送响应数据。
- session对象 :用于在多个请求之间保存用户会话信息。
示例代码 :
<%
String name = request.getParameter("name");
if (name != null) {
session.setAttribute("user", name);
response.sendRedirect("welcome.jsp");
}
%>
代码逻辑分析 :
- 使用 request.getParameter("name") 获取表单参数。
- 将用户名存入 session 对象中。
- 使用 response.sendRedirect() 进行页面重定向。
4.2.2 application与pageContext的作用域分析
JSP有四种作用域:
| 作用域对象 | 生存周期 | 作用范围 |
|---|---|---|
page |
当前页面 | 仅限当前JSP页面 |
request |
一次请求 | 同一个请求内的所有资源 |
session |
会话期间 | 同一个用户的多个请求 |
application |
应用启动到关闭 | 整个Web应用的所有用户 |
示例代码 :
<%
pageContext.setAttribute("pageAttr", "This is page scope");
request.setAttribute("requestAttr", "This is request scope");
session.setAttribute("sessionAttr", "This is session scope");
application.setAttribute("appAttr", "This is application scope");
%>
4.2.3 EL表达式与JSTL标签的引入
JSP表达式语言(EL)提供了一种简洁的方式访问页面数据。例如:
<p>用户名:${sessionScope.user}</p>
JSTL(JSP标准标签库)提供了更强大的功能,例如条件判断、循环等。
引入JSTL :
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
使用示例 :
<c:if test="${not empty user}">
<p>欢迎回来,${user}</p>
</c:if>
4.3 JSP与Servlet协作开发
JSP和Servlet是Java Web开发的两大核心技术。JSP负责页面展示,Servlet负责业务逻辑,两者结合可实现MVC架构。
4.3.1 MVC模式下的页面跳转机制
MVC(Model-View-Controller)是一种分层架构模式:
- Model(模型) :处理数据和业务逻辑。
- View(视图) :负责用户界面展示。
- Controller(控制器) :接收请求并协调模型与视图。
流程图 :
graph TD
A[用户请求] --> B(Servlet控制器)
B --> C[处理业务逻辑]
C --> D{是否成功}
D -- 是 --> E[JSP页面渲染]
D -- 否 --> F[错误页面]
E --> G[返回响应]
F --> G
4.3.2 数据传递与页面渲染
Servlet中处理完业务逻辑后,可以通过 request.setAttribute() 将数据传递给JSP页面,再使用EL表达式或JSTL进行渲染。
Servlet代码示例 :
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<String> users = Arrays.asList("Alice", "Bob", "Charlie");
request.setAttribute("users", users);
request.getRequestDispatcher("/users.jsp").forward(request, response);
}
JSP页面渲染 :
<c:forEach items="${users}" var="user">
<p>${user}</p>
</c:forEach>
4.3.3 JSP与Servlet的联合调试技巧
- 使用System.out.println()输出日志 :适用于简单调试。
- 使用IDE断点调试 :推荐在Eclipse或IntelliJ中设置断点进行调试。
- 查看服务器日志文件 :Tomcat的
catalina.out文件记录Servlet执行日志。 - 使用JSP页面调试信息 :可在JSP中加入调试语句查看变量值。
4.4 JSP页面优化与安全性
随着Web应用的发展,JSP页面的性能与安全性变得尤为重要。合理的优化和安全措施能够显著提升应用的整体表现。
4.4.1 页面缓存控制与性能提升
JSP页面可以通过设置HTTP响应头来控制浏览器缓存行为。
示例代码 :
<%
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
%>
优化建议 :
- 静态资源(如图片、CSS、JS)启用浏览器缓存。
- 使用JSP预编译,避免首次访问时的延迟。
- 减少JSP页面中的Java脚本,改用JSTL和EL表达式。
4.4.2 防止JSP页面被直接访问
为了防止用户直接访问某些JSP页面(如包含业务逻辑的页面),可以将其放置在 WEB-INF 目录下,该目录下的内容不能被直接访问。
web.xml配置 :
<jsp-config>
<jsp-property-group>
<url-pattern>/WEB-INF/*.jsp</url-pattern>
<is-xml>false</is-xml>
</jsp-property-group>
</jsp-config>
替代访问方式 :
- 通过Servlet转发请求到 WEB-INF 目录下的JSP页面。
request.getRequestDispatcher("/WEB-INF/secure.jsp").forward(request, response);
安全建议 :
- 不在JSP中直接处理敏感数据。
- 对用户输入进行过滤和转义,防止XSS攻击。
- 使用HTTPS协议保护数据传输安全。
本章系统讲解了JSP的基础语法、内置对象、与Servlet的协作机制以及页面优化与安全策略。通过这些内容的学习,开发者可以构建出结构清晰、性能良好、安全可靠的动态Web页面,为后续MVC架构与框架开发打下坚实基础。
5. MVC设计模式应用与实现
5.1 MVC设计模式概述
MVC(Model-View-Controller)是一种经典的软件设计模式,广泛应用于Web开发中,旨在实现业务逻辑、用户界面和用户输入之间的解耦。该模式将应用程序划分为三个核心组件:
- Model(模型) :负责数据的处理与存储,通常包含业务逻辑和数据库交互。模型的变化会通知视图进行更新。
- View(视图) :负责数据的展示,通常由JSP页面实现,负责将模型数据渲染为用户可感知的界面。
- Controller(控制器) :负责接收用户的输入(如HTTP请求),调用模型处理业务逻辑,并决定视图的跳转。
MVC在Web应用中的优势
| 优势 | 说明 |
|---|---|
| 职责清晰 | 各组件职责明确,便于维护与协作 |
| 可扩展性强 | 各层之间低耦合,便于模块化开发和功能扩展 |
| 易于测试 | 各层可以独立进行单元测试 |
| 代码复用性高 | 模型和控制器可以被多个视图复用 |
应用场景
- 企业级Web应用开发
- 多人协作开发项目
- 需要长期维护和迭代的项目
5.2 基于Servlet+JSP的MVC实现
在Java Web中,Servlet通常作为控制器,JSP作为视图,JavaBean作为模型来实现MVC模式。
5.2.1 控制器层(Servlet)的设计与实现
控制器层负责接收请求、调用模型处理业务逻辑、转发请求至视图。
@WebServlet("/user")
public class UserController extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if ("list".equals(action)) {
List<User> users = UserService.getAllUsers(); // 调用模型层
request.setAttribute("users", users);
request.getRequestDispatcher("/userList.jsp").forward(request, response);
} else if ("detail".equals(action)) {
int id = Integer.parseInt(request.getParameter("id"));
User user = UserService.getUserById(id);
request.setAttribute("user", user);
request.getRequestDispatcher("/userDetail.jsp").forward(request, response);
}
}
}
@WebServlet("/user"):注解方式配置Servlet映射路径。doGet:处理GET请求。request.getParameter("action"):获取请求动作参数。request.setAttribute(...):将数据存入request作用域,供JSP使用。request.getRequestDispatcher(...).forward(...):请求转发至JSP页面。
5.2.2 模型层(JavaBean)的数据封装
模型层负责封装数据和业务逻辑,常见为POJO(Plain Old Java Object)。
public class User {
private int id;
private String name;
private String email;
// 构造方法、getter、setter省略
}
public class UserService {
public static List<User> getAllUsers() {
// 模拟从数据库获取数据
List<User> userList = new ArrayList<>();
userList.add(new User(1, "Tom", "tom@example.com"));
userList.add(new User(2, "Jerry", "jerry@example.com"));
return userList;
}
public static User getUserById(int id) {
// 模拟根据ID查询
return new User(id, "User" + id, "user" + id + "@example.com");
}
}
User类:JavaBean,用于封装用户信息。UserService类:模拟业务逻辑层,负责数据的获取。
5.2.3 视图层(JSP)的渲染与数据绑定
视图层使用JSP页面展示数据,通常结合JSTL和EL表达式简化数据绑定。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>User List</title>
</head>
<body>
<h2>User List</h2>
<table border="1">
<tr>
<th>ID</th><th>Name</th><th>Email</th>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.email}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
<c:forEach>:JSTL循环标签,用于遍历用户列表。${user.id}:EL表达式,用于获取JavaBean属性。
5.3 MVC框架的进阶实践
5.3.1 分层架构与模块化开发
在大型项目中,MVC模式通常会进一步分层,形成更清晰的结构:
Controller → Service → DAO → Model
- Controller :接收请求,调用Service。
- Service :处理业务逻辑,调用DAO。
- DAO(Data Access Object) :与数据库交互。
- Model :实体类,用于封装数据。
5.3.2 使用MVC模式开发用户管理系统
以用户管理系统为例,展示MVC结构的完整流程:
- 用户访问URL :如
/user?action=list - Servlet接收请求 :
UserController - 调用Service获取数据 :
UserService.getAllUsers() - Service调用DAO :
UserDAO.selectAll() - 返回数据至Servlet :封装到request作用域
- 转发至JSP页面渲染 :显示用户列表
5.3.3 异常处理与日志记录在MVC中的集成
在实际开发中,异常处理和日志记录是不可或缺的部分。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String action = request.getParameter("action");
if ("list".equals(action)) {
List<User> users = UserService.getAllUsers();
request.setAttribute("users", users);
request.getRequestDispatcher("/userList.jsp").forward(request, response);
}
} catch (Exception e) {
// 日志记录
e.printStackTrace();
// 设置错误信息并跳转
request.setAttribute("error", "系统异常,请稍后再试");
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
- 使用
try-catch捕获异常。 - 使用
e.printStackTrace()记录日志(可替换为Log4j或SLF4J)。 - 将错误信息传递给JSP页面展示。
5.4 MVC与现代Web框架的对比
5.4.1 Spring MVC与传统MVC的异同
| 对比项 | 传统MVC(Servlet+JSP) | Spring MVC |
|---|---|---|
| 控制器 | 自定义Servlet | @Controller 注解类 |
| 请求映射 | web.xml或@WebServlet | @RequestMapping |
| 数据绑定 | 手动封装 | 自动绑定 |
| 异常处理 | 手动try-catch | @ControllerAdvice 统一处理 |
| 视图解析 | request.getRequestDispatcher | ViewResolver自动解析 |
| 配置方式 | web.xml+注解 | Spring配置文件或Java Config |
5.4.2 教材源码中的MVC实例分析
以教材中的用户管理系统为例,其源码目录结构如下:
src/
├── controller/
│ └── UserController.java
├── service/
│ └── UserService.java
├── dao/
│ └── UserDao.java
├── model/
│ └── User.java
WebContent/
├── WEB-INF/
│ └── web.xml
├── userList.jsp
└── userDetail.jsp
通过该结构可以清晰看到MVC三层架构的划分,每个模块职责明确,便于后期扩展和维护。
通过本章的学习,读者应能掌握基于Servlet+JSP的MVC实现方式,理解其在Web开发中的结构优势,并能将其应用于实际项目中。
简介:《Java Web开发实战教材源码》是一份涵盖Java Web开发全流程的学习资源,共包含15个章节,每个章节围绕一个主题展开,帮助学习者深入掌握Java在Web开发中的核心应用。内容涵盖Java基础、Servlet与JSP、MVC架构、JDBC数据库连接、JSTL与EL表达式、过滤器与监听器、会话管理、Ajax与jQuery交互、Web部署与容器、以及Web安全防护等关键技术。通过该源码的实战学习,开发者可以全面掌握Java Web开发技能,提升实际项目开发能力,适用于初学者和进阶者的学习与提升。
更多推荐



所有评论(0)