本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:《Java Web开发实战教材源码》是一份涵盖Java Web开发全流程的学习资源,共包含15个章节,每个章节围绕一个主题展开,帮助学习者深入掌握Java在Web开发中的核心应用。内容涵盖Java基础、Servlet与JSP、MVC架构、JDBC数据库连接、JSTL与EL表达式、过滤器与监听器、会话管理、Ajax与jQuery交互、Web部署与容器、以及Web安全防护等关键技术。通过该源码的实战学习,开发者可以全面掌握Java 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 的执行流程如下:

  1. 客户端发送请求 :浏览器通过 HTTP 协议向服务器发起请求。
  2. 服务器接收请求 :服务器将请求交给对应的 Servlet 容器处理。
  3. 加载或创建 Servlet 实例 :如果该 Servlet 尚未被加载,则容器加载类并创建实例。
  4. 调用 init() 方法 :容器调用 init() 方法初始化 Servlet,仅执行一次。
  5. 调用 service() 方法 :容器调用 service() 方法处理请求。该方法会根据请求类型(GET、POST 等)调用相应的 doGet() doPost() 方法。
  6. 生成响应 :Servlet 处理完成后,通过 HttpServletResponse 对象将响应返回给客户端。
  7. 销毁 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页面的执行流程如下:

  1. 请求到达服务器 :客户端发起对JSP页面的请求。
  2. JSP解析与编译 :服务器将JSP页面翻译为Servlet源代码,并编译为字节码文件。
  3. Servlet执行 :编译后的Servlet被加载并执行,生成HTML响应。
  4. 响应返回客户端 :最终生成的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结构的完整流程:

  1. 用户访问URL :如 /user?action=list
  2. Servlet接收请求 UserController
  3. 调用Service获取数据 UserService.getAllUsers()
  4. Service调用DAO UserDAO.selectAll()
  5. 返回数据至Servlet :封装到request作用域
  6. 转发至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开发中的结构优势,并能将其应用于实际项目中。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:《Java Web开发实战教材源码》是一份涵盖Java Web开发全流程的学习资源,共包含15个章节,每个章节围绕一个主题展开,帮助学习者深入掌握Java在Web开发中的核心应用。内容涵盖Java基础、Servlet与JSP、MVC架构、JDBC数据库连接、JSTL与EL表达式、过滤器与监听器、会话管理、Ajax与jQuery交互、Web部署与容器、以及Web安全防护等关键技术。通过该源码的实战学习,开发者可以全面掌握Java Web开发技能,提升实际项目开发能力,适用于初学者和进阶者的学习与提升。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐