小白也能看懂的-Java Web开发全攻略(超详细)

个人主页:java之路-CSDN博客(期待您的关注)

 

目录

Java Web 开发初印象

Java Web 开发的核心技术

(一)Servlet

(二)JSP

(三)JavaBean

(四)MVC 架构

常用 Java Web 开发框架

(一)Spring Framework

(二)Spring Boot

(三)Hibernate

(四)Struts

Java Web 开发实战

(一)需求分析

(二)系统设计

(三)编码实现

(四)测试与部署

(二)依赖冲突

(三)HTTP 请求方法不支持


Java Web 开发初印象

小白也能看懂的-Java Web开发全攻略(超详细)_第1张图片

在当今这个互联网飞速发展的时代,Web 应用已经深入到我们生活的方方面面,从日常使用的电商平台、社交媒体,到企业内部的管理系统,无一不是 Web 应用的体现。而 Java Web 开发,凭借其卓越的特性,在 Web 开发领域占据着举足轻重的地位。

Java 语言自 1995 年诞生以来,凭借 “一次编写,到处运行” 的特性,也就是通过 Java 虚拟机(JVM)实现跨平台运行,这一特性使得基于 Java 开发的 Web 应用可以轻松部署在不同的操作系统上,极大地提高了软件的可移植性,减少了开发和维护成本。同时,Java 拥有庞大且丰富的类库和框架生态系统,这为开发者提供了极大的便利。在企业级开发中,稳定性和安全性是至关重要的,Java 从设计之初就注重这些方面,内置的安全管理器和类加载器等机制可以有效防止恶意代码的运行,为 Web 应用的安全稳定运行保驾护航。 此外,Java 还拥有庞大的开发者社区,当开发者在开发过程中遇到问题时,可以在社区中迅速获得帮助,同时也有大量的开源项目可供参考和使用,这进一步推动了 Java Web 开发的发展和创新。

Java Web 开发的核心技术

(一)Servlet

Servlet 是 Java Web 开发中的基础组件,它是运行在服务器端的 Java 程序,主要用于处理客户端的请求并生成动态的 Web 内容 。其本质上是一个实现了Servlet接口的 Java 类,狭义上指这个接口,广义上则指任何实现该接口的类。在绝大多数情况下,Servlet 被用来扩展基于 HTTP 协议的 Web 服务器。

Servlet 的生命周期由 Servlet 容器(如 Tomcat)来管理,从创建到销毁,大致经历以下几个阶段:

  • 初始化阶段:当 Servlet 被第一次请求或者在容器启动时(如果配置了load-on-startup),容器会创建 Servlet 实例,并调用其init()方法进行初始化操作,这个方法在 Servlet 的整个生命周期中只会被调用一次,主要用于加载资源、初始化数据库连接等。
  • 服务阶段:每次有客户端请求到达时,容器会调用 Servlet 的service()方法来处理请求,service()方法会根据请求的类型(如 GET、POST 等),调用相应的doGet()、doPost()等方法来生成响应内容。
  • 销毁阶段:当 Servlet 容器关闭或者 Servlet 需要被重新加载时,容器会调用 Servlet 的destroy()方法,该方法用于释放 Servlet 占用的资源,如关闭数据库连接、停止后台线程等 。之后,Servlet 实例会被标记为垃圾回收,等待被 JVM 回收。

下面通过一个简单的代码示例来展示如何创建和使用 Servlet:

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.io.PrintWriter;

@WebServlet("/hello")

public class HelloServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out.println("");

out.println("

Hello, Java Web!

"); out.println(""); } }

在上述代码中,我们创建了一个名为HelloServlet的 Servlet,它使用了@WebServlet注解来映射 URL 路径为/hello。当客户端通过浏览器访问/hello时,会调用doGet()方法,在页面上输出 "Hello, Java Web!"。

(二)JSP

JSP(JavaServer Pages)是一种动态网页开发技术,它允许在传统的 HTML 页面中嵌入 Java 代码和 JSP 标签,后缀名为.jsp 。JSP 本质上是 Servlet 的扩展,它在服务器端运行,最终会被翻译成 Servlet 代码并执行,然后返回一个 HTML 文件给客户端浏览器显示。

JSP 具有以下特点:

  • 内容生成与显示分离:Web 页面开发人员可以使用 HTML 或者 XML 标识来设计和格式化最终页面,而将动态内容的生成通过嵌入的 Java 代码来实现,使得业务逻辑与页面设计分离,提高了代码的可维护性和可扩展性。
  • 强调可重用组件:JSP 页面可以依赖于可重用的、跨平台的组件(如 JavaBean)来执行更为复杂的处理,减少了重复代码的编写。
  • 采用标识简化页面开发:通过 JSP 标签,Web 页面开发人员可以更方便地实现一些功能,如访问数据库、访问 JavaBean 组件、在不同网页之间传递和共享信息等,即使不熟悉复杂脚本语言编程的人员也能轻松上手。

在 JSP 页面中,可以通过以下几种方式嵌入 Java 代码:

  • 声明变量或方法:使用<%! 声明; %>,这种方式声明的变量或方法是 JSP 生成类的成员变量或成员方法,作用域为整个 JSP 页面。例如:
    <%!
    
    int count = 0;
    
    public int add(int a, int b) {
    
    return a + b;
    
    }
    
    %>

  • Java 代码片段:使用<% java代码; %>,这些代码会被直接嵌入到_jspService()方法中,在请求处理期间执行。例如:
    <%
    
    for (int i = 0; i < 5; i++) {
    
    out.println("

    这是第 " + (i + 1) + " 行

    "); } %>

  • 表达式:使用<%= 表达式 %>,用于将表达式的值输出到页面上,表达式会被转换为 String 类型并插入到表达式出现的地方。例如:
    <%= new java.util.Date() %>

下面是一个简单的 JSP 页面示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>





JSP示例





欢迎来到JSP页面

<% String message = "这是来自JSP的动态消息"; %>

<%= message %>

在这个示例中,我们在 JSP 页面中嵌入了 Java 代码,定义了一个字符串变量message,并通过表达式将其输出到页面上。

(三)JavaBean

JavaBean 是一种遵循特定规范的 Java 类,它主要用于封装数据,提供访问数据的方法(getter 和 setter 方法),并且可以被其他程序重用 。JavaBean 通常具有以下特性:

  • 公共性:JavaBean 类必须是公开的(public),以便被其他类访问。
  • 无参构造函数:必须有一个无参数的公共构造函数,以便可以通过反射机制或其他方式实例化对象。
  • 属性封装:属性通常是私有的(private),通过公共的 getter 和 setter 方法来访问和修改这些属性的值,这样可以保证数据的安全性和封装性。
  • 可重用性:可以在不同的模块或组件之间被重复使用,作为数据传递、业务逻辑处理的载体。

例如,我们创建一个简单的 JavaBean 类来封装用户信息:

public class UserBean implements java.io.Serializable {

private String name;

private int age;

private String email;

// 无参构造函数

public UserBean() {}

// Getter方法

public String getName() {

return name;

}

public int getAge() {

return age;

}

public String getEmail() {

return email;

}

// Setter方法

public void setName(String name) {

this.name = name;

}

public void setAge(int age) {

this.age = age;

}

public void setEmail(String email) {

this.email = email;

}

}

在 Java Web 开发中,JavaBean 常被用于在不同层之间传递数据,比如在 JSP 和 Servlet 之间。在 JSP 页面中,可以使用标签来创建 JavaBean 的实例,并通过标签来设置和获取其属性值。例如:









姓名:
年龄:
邮箱:

(四)MVC 架构

MVC(Model-View-Controller)架构是一种软件设计规范,它通过将应用程序的逻辑、数据和用户界面展示进行分离,使得软件开发更加模块化、可维护性和可重用性更高 。在 Java Web 开发中,MVC 架构被广泛应用,下面来详细介绍其原理和应用。

  • 模型(Model):负责处理应用程序的数据和业务逻辑。它从数据库或其他数据源获取数据,对数据进行处理和验证,然后将数据传递给控制器层。模型还可以定义数据访问方法,用于从数据库中检索或更新数据。例如,在一个电商应用中,模型可能包含商品信息的获取、订单的处理等业务逻辑。
  • 视图(View):是用户看到的部分,负责显示模型的数据。在 Java Web 中,视图通常是由 HTML、CSS 和 JavaScript 等前端技术实现的,也可以使用 JSP、Thymeleaf 等模板引擎来生成动态页面。视图只负责数据的展示,不包含任何业务逻辑。比如电商应用中的商品展示页面、订单确认页面等。
  • 控制器(Controller):是模型和视图之间的桥梁,负责接收用户的请求,处理业务逻辑,并将结果传递给视图。它接收用户的请求,根据请求的类型和参数调用相应的业务逻辑(在模型中),然后选择合适的视图来展示处理结果。在 Java Web 开发中,Servlet 常被用作控制器,现在也有很多基于 MVC 架构的框架(如 Spring MVC)提供了更强大的控制器功能。例如,在电商应用中,用户点击 “提交订单” 按钮,控制器会接收这个请求,调用模型中的订单处理逻辑,然后将处理结果(如订单是否提交成功)传递给相应的视图(如订单提交成功页面或失败提示页面)。

以一个简单的用户登录功能为例,MVC 架构的工作流程如下:

  1. 用户在浏览器中输入用户名和密码,点击登录按钮,向服务器发送请求。
  2. 服务器上的控制器(如 Servlet)接收到请求,获取用户名和密码参数。
  3. 控制器调用模型中的业务逻辑(如用户验证方法),将用户名和密码传递给模型进行验证。
  4. 模型与数据库进行交互,验证用户名和密码是否正确,并返回验证结果给控制器。
  5. 控制器根据验证结果选择相应的视图,如果验证成功,选择用户主页视图;如果验证失败,选择登录失败提示视图。
  6. 视图将数据渲染成用户可以看到的网页,返回给用户浏览器进行展示。

在 Java Web 开发中,使用 MVC 架构可以使代码结构更加清晰,不同模块之间的职责明确,便于团队开发和维护。同时,也提高了代码的可重用性和可扩展性,当业务逻辑或用户界面发生变化时,只需要修改相应的模块,而不会影响其他部分的代码。

常用 Java Web 开发框架

小白也能看懂的-Java Web开发全攻略(超详细)_第2张图片

(一)Spring Framework

Spring Framework 是一个开源的轻量级 Java 开发框架,它提供了全面的编程和配置模型,适用于 Java 企业级应用开发 。其核心特性主要包括依赖注入(DI)和面向切面编程(AOP)。

依赖注入(DI):也称为控制反转(IoC),是 Spring 框架的核心思想之一。它通过将对象的创建和依赖关系的管理从应用程序代码中分离出来,交给 Spring 容器来负责,从而降低了组件之间的耦合度,提高了代码的可维护性和可测试性 。在传统的 Java 开发中,对象之间的依赖关系通常是在代码中通过new关键字来创建和管理的,这使得代码的耦合度很高,当依赖关系发生变化时,需要修改大量的代码。而使用依赖注入,我们只需要在 Spring 的配置文件(如 XML 或 Java 配置类)中声明对象之间的依赖关系,Spring 容器会在运行时自动创建和注入这些依赖对象。例如,假设有一个UserService类依赖于UserRepository类来进行用户数据的访问:

public class UserService {

private UserRepository userRepository;

// 构造函数注入

public UserService(UserRepository userRepository) {

this.userRepository = userRepository;

}

// 业务方法

public void saveUser(User user) {

userRepository.save(user);

}

}

在 Spring 的配置文件中,可以这样配置:







通过上述配置,Spring 容器会在创建userService实例时,自动将userRepository实例注入到userService中,UserService类不需要关心UserRepository的创建和初始化过程。除了构造函数注入,Spring 还支持 Setter 方法注入和基于注解的自动装配(如@Autowired、@Resource等)。

面向切面编程(AOP):AOP 是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、权限控制等)从业务逻辑中分离出来,以提高代码的模块化和可维护性 。在传统的面向对象编程中,这些横切关注点通常会分散在各个业务逻辑类中,导致代码的重复和混乱。Spring 的 AOP 通过代理模式和动态字节码生成技术,实现了对目标对象方法的增强。例如,我们可以通过 AOP 实现一个日志切面,在方法执行前后记录日志:

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

@Aspect

@Component

public class LoggingAspect {

private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

@Around("@annotation(com.example.Loggable)")

public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {

logger.info("开始执行方法: {}", joinPoint.getSignature().getName());

try {

Object result = joinPoint.proceed();

logger.info("方法执行结束,返回结果: {}", result);

return result;

} catch (Exception e) {

logger.error("方法执行出错: {}", e.getMessage());

throw e;

}

}

}

在上述代码中,我们定义了一个切面类LoggingAspect,使用@Aspect注解标识它是一个切面,@Around注解定义了一个环绕通知,当被@Loggable注解标记的方法执行时,会触发这个通知,在方法执行前后记录日志信息。

(二)Spring Boot

Spring Boot 是基于 Spring 框架的快速开发框架,它的出现大大简化了 Spring 应用的搭建和开发过程,遵循 “约定大于配置” 的原则,让开发者能够快速构建出生产级别的应用程序 。

Spring Boot 具有以下显著优势:

  • 快速开发:提供了一系列的 Starter 依赖,开发者只需引入相关的 Starter,Spring Boot 会自动配置好相应的依赖和环境,减少了大量的手动配置工作。例如,要创建一个 Spring Boot Web 应用,只需引入spring-boot-starter-web依赖,Spring Boot 会自动配置好 Spring MVC、Tomcat 等相关组件,开发者可以快速开始编写业务代码。
  • 自动配置:根据项目的依赖和类路径,Spring Boot 会自动配置应用所需的各种组件,如数据源、数据库连接池、消息队列等,开发者无需编写大量的 XML 或 Java 配置代码。例如,当引入spring-boot-starter-jdbc和数据库驱动依赖后,Spring Boot 会自动配置好数据源和 JdbcTemplate,开发者可以直接使用JdbcTemplate进行数据库操作。
  • 内嵌容器:Spring Boot 内置了 Tomcat、Jetty 等 Servlet 容器,应用可以直接打包成可执行的 JAR 文件,通过java -jar命令即可运行,无需手动部署到外部容器中,简化了应用的部署过程。
  • 生产就绪特性:提供了诸如健康检查、指标监控、外部化配置等功能,方便应用在生产环境中的管理和运维。例如,通过引入spring-boot-starter-actuator依赖,可以启用健康检查端点(如/actuator/health),用于检查应用的运行状态,还可以通过配置文件实现外部化配置,方便在不同环境下切换配置参数。

下面通过一个简单的示例展示如何使用 Spring Boot 搭建一个 Web 应用:

  1. 创建 Spring Boot 项目:可以使用 Spring Initializr(https://start.spring.io/ )或者在 IDE(如 IntelliJ IDEA、Eclipse)中创建 Spring Boot 项目。在创建过程中,选择Web依赖,这样项目会自动引入spring-boot-starter-web依赖。
  2. 编写控制器:在项目的src/main/java目录下,创建一个控制器类,例如HelloController.java:
    import org.springframework.web.bind.annotation.GetMapping;
    
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    
    public class HelloController {
    
    @GetMapping("/hello")
    
    public String hello() {
    
    return "Hello, Spring Boot!";
    
    }
    
    }

在上述代码中,@RestController注解表示这是一个 RESTful 风格的控制器,@GetMapping("/hello")注解将hello方法映射到/hello路径,当访问/hello时,会返回 "Hello, Spring Boot!"。

3. 运行应用:在 IDE 中直接运行 Spring Boot 应用的入口类(通常是带有@SpringBootApplication注解的类),或者将项目打包成 JAR 文件后,通过java -jar命令运行。运行成功后,在浏览器中访问http://localhost:8080/hello(默认端口是 8080),即可看到返回的结果。

(三)Hibernate

Hibernate 是一个对象关系映射(ORM)框架,它的主要作用是将 Java 对象与关系型数据库中的表进行映射,使得开发者可以使用面向对象的方式来操作数据库,而无需编写大量的 SQL 语句 。通过 Hibernate,我们可以将 Java 对象的属性映射到数据库表的列,将对象的持久化操作(如保存、更新、删除、查询)转换为对数据库的操作。

使用 Hibernate 的好处主要有以下几点:

  • 简化数据库操作:开发者只需要关注 Java 对象的操作,而不需要编写复杂的 SQL 语句,提高了开发效率。例如,保存一个 Java 对象,只需要调用session.save(object)方法,Hibernate 会自动生成对应的 SQL 语句并执行。
  • 提高代码的可维护性和可移植性:由于 Hibernate 屏蔽了不同数据库之间的差异,通过 Hibernate 编写的代码可以很容易地在不同的数据库之间切换,如从 MySQL 切换到 Oracle,只需要修改配置文件中的数据库连接信息和方言即可,而不需要修改大量的业务代码。
  • 提供缓存机制:Hibernate 提供了一级缓存(Session 缓存)和二级缓存(SessionFactory 缓存),可以减少对数据库的访问次数,提高应用的性能。例如,在同一个 Session 中多次查询同一个对象,Hibernate 会先从一级缓存中获取,如果缓存中有则直接返回,而不会再次查询数据库。

下面通过一个简单的示例展示如何使用 Hibernate 进行数据库操作:

  1. 添加依赖:在项目的pom.xml文件中添加 Hibernate 相关的依赖,如hibernate-core、hibernate-entitymanager以及数据库驱动依赖(如 MySQL 驱动)。
  2. 配置 Hibernate:在src/main/resources目录下创建hibernate.cfg.xml配置文件,配置数据库连接信息、方言、映射文件等。例如:
    
    
    
    
    
    
    
    
    com.mysql.cj.jdbc.Driver
    
    jdbc:mysql://localhost:3306/mydb
    
    root
    
    password
    
    
    
    org.hibernate.dialect.MySQL8Dialect
    
    
    
    true
    
    
    
    
    
    
    
    

  3. 创建实体类和映射文件:创建一个 Java 实体类,如User.java,并为其创建对应的映射文件User.hbm.xml。例如:
    public class User {
    
    private Long id;
    
    private String name;
    
    private int age;
    
    // 构造函数、Getter和Setter方法
    
    public User() {}
    
    public User(String name, int age) {
    
    this.name = name;
    
    this.age = age;
    
    }
    
    public Long getId() {
    
    return id;
    
    }
    
    public void setId(Long id) {
    
    this.id = id;
    
    }
    
    public String getName() {
    
    return name;
    
    }
    
    public void setName(String name) {
    
    this.name = name;
    
    }
    
    public int getAge() {
    
    return age;
    
    }
    
    public void setAge(int age) {
    
    this.age = age;
    
    }
    
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

  4. 进行数据库操作:通过 Hibernate 的Session和SessionFactory进行数据库操作,如保存用户信息:
    import org.hibernate.Session;
    
    import org.hibernate.SessionFactory;
    
    import org.hibernate.Transaction;
    
    import org.hibernate.cfg.Configuration;
    
    public class HibernateExample {
    
    public static void main(String[] args) {
    
    // 创建SessionFactory
    
    SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
    
    Session session = sessionFactory.openSession();
    
    Transaction transaction = null;
    
    try {
    
    transaction = session.beginTransaction();
    
    User user = new User("张三", 25);
    
    session.save(user);
    
    transaction.commit();
    
    System.out.println("用户保存成功");
    
    } catch (Exception e) {
    
    if (transaction!= null) {
    
    transaction.rollback();
    
    }
    
    e.printStackTrace();
    
    } finally {
    
    session.close();
    
    sessionFactory.close();
    
    }
    
    }
    
    }

(四)Struts

Struts 是一个基于 MVC 架构的 Java Web 应用框架,它主要用于创建企业级的 Web 应用程序 。Struts 框架的特点和优势主要体现在以下几个方面:

  • MVC 架构的良好实现:Struts 框架将应用程序分为模型(Model)、视图(View)和控制器(Controller)三个部分,使得代码结构清晰,各部分职责明确,便于开发、维护和扩展。在 Struts 中,控制器主要由ActionServlet和Action类组成,ActionServlet负责接收用户请求,并根据配置文件将请求转发到相应的Action类进行处理;模型可以是 JavaBean 或 EJB 等,用于封装业务数据和逻辑;视图通常由 JSP、FreeMarker 等技术实现,用于展示数据给用户。
  • 丰富的标签库:Struts 提供了一套丰富的标签库,如 HTML 标签库、Bean 标签库、Logic 标签库等,这些标签库可以简化 JSP 页面的开发,提高代码的可读性和可维护性。例如,使用 HTML 标签库可以方便地生成 HTML 表单元素,并且可以与后台的ActionForm对象进行数据绑定;Logic 标签库可以用于在 JSP 页面中进行条件判断、循环等逻辑操作。
  • 灵活的配置:Struts 通过配置文件(如struts.xml)来管理应用的各种配置信息,包括Action的映射、ActionForm的配置、全局转发等,开发者可以根据需求灵活地进行配置。例如,可以在struts.xml中配置不同的Action对应不同的请求路径,以及请求处理完成后的转发或重定向路径。

下面通过一个简单的示例展示 Struts 的基本使用方法:

  1. 添加 Struts 依赖:在项目的pom.xml文件中添加 Struts 相关的依赖,如struts-core、struts-taglib等。
  2. 配置 Struts:在src/main/resources目录下创建struts.xml配置文件,配置Action和视图的映射关系。例如:
    
    
    
    
    
    
    
    
    /success.jsp
    
    /error.jsp
    
    
    
    
    
    

在上述配置中,定义了一个名为login的Action,对应的类是com.example.LoginAction,当execute方法执行成功时,跳转到success.jsp页面,执行失败时跳转到error.jsp页面。

3. 创建 Action 类:创建一个Action类,如LoginAction.java,用于处理用户的登录请求:

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {

private String username;

private String password;

// Getter和Setter方法

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;

}

@Override

public String execute() throws Exception {

if ("admin".equals(username) && "123456".equals(password)) {

return SUCCESS;

} else {

return ERROR;

}

}

}
  1. 创建视图页面:创建success.jsp和error.jsp页面,用于展示登录结果:
    
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
    
    
    
    登录成功
    
    
    
    
    
    

    欢迎,${username}

    <%@ page contentType="text/html;charset=UTF-8" language="java" %> 登录失败

    用户名或密码错误

  2. 部署和运行:将项目部署到 Web 服务器(如 Tomcat)上,启动服务器后,在浏览器中访问http://localhost:8080/项目名/login.action,输入用户名和密码进行登录,根据登录结果会跳转到相应的页面。

Java Web 开发实战

小白也能看懂的-Java Web开发全攻略(超详细)_第3张图片

(一)需求分析

以在线图书商城为例,来深入分析项目的需求和功能模块。在当今数字化阅读和便捷购物的大趋势下,在线图书商城应运而生,为广大读者提供了一个便捷、高效的购书平台。

从用户的角度来看,核心需求是能够方便快捷地浏览和搜索各类图书,查看图书的详细信息,包括书名、作者、出版社、出版日期、内容简介、读者评价等,以便做出购买决策。同时,用户需要具备注册和登录功能,这样可以保存个人的购买历史、收藏喜欢的图书,以及享受个性化的推荐服务。在购物过程中,购物车功能必不可少,用户可以将心仪的图书加入购物车,随时调整购买数量,最后进行结算。结算时,需要支持多种支付方式,如常见的微信支付、支付宝支付、银行卡支付等,确保支付的安全和便捷。

对于管理员而言,需要有强大的后台管理功能。在图书管理方面,能够添加新的图书信息,包括上传图书封面图片、录入详细的图书描述等;可以对已有的图书信息进行修改,如更新图书价格、库存数量等;对于下架或不再销售的图书,能够进行删除操作。订单管理也是重要的一环,管理员要能够查看所有用户的订单信息,包括订单状态(如待付款、待发货、已发货、已完成等),并可以对订单状态进行更新,处理订单的发货、退货等事宜。此外,管理员还需要管理用户信息,如审核新注册用户,封禁违规用户等,以维护商城的良好秩序。

综合以上需求,在线图书商城可以划分为以下几个主要功能模块:

  • 用户模块:包含用户注册、登录、密码找回、个人信息管理(如修改个人资料、查看购买历史、收藏图书)等功能。
  • 图书模块:实现图书的展示(热门图书推荐、新书推荐、分类展示)、搜索(按书名、作者、出版社、关键词搜索)、详情查看等功能。
  • 购物车模块:用户将图书添加到购物车,在购物车中修改图书数量、删除图书,以及计算购物车中图书的总价等。
  • 订单模块:用户提交订单,生成订单详情,包括订单编号、下单时间、收货地址、收货人信息、购买图书清单及价格等;管理员对订单进行管理,如查看订单、更新订单状态、处理退款退货等。
  • 支付模块:集成多种支付接口,实现安全、便捷的支付功能,并处理支付结果的回调和记录。
  • 后台管理模块:除了上述的图书管理、订单管理和用户管理功能外,还包括系统设置(如网站公告发布、系统参数配置)等功能。

(二)系统设计

在进行系统架构设计时,需要综合考虑系统的性能、可扩展性、可维护性等多方面因素。基于 Java Web 开发的技术栈,结合在线图书商城的业务需求,我们可以采用以下设计方案:

技术选型

  • 后端框架:选用 Spring Boot 框架,它的快速开发特性和自动配置功能能够大大提高开发效率,减少配置的繁琐工作。同时,Spring Boot 与 Spring 生态系统的其他组件(如 Spring Data、Spring Security 等)无缝集成,方便构建功能强大的企业级应用。
  • 持久层框架:采用 MyBatis,它是一个优秀的 ORM 框架,支持自定义 SQL 语句,灵活性高,能够很好地满足复杂的数据库操作需求。通过 MyBatis 的映射文件,可以将 Java 对象与数据库表进行灵活的映射,实现数据的持久化操作。
  • 数据库:选择 MySQL 关系型数据库,它具有开源、稳定、性能良好等优点,能够满足在线图书商城的数据存储和管理需求。MySQL 支持事务处理,确保数据的完整性和一致性,同时提供了丰富的索引和查询优化功能,提高数据查询的效率。
  • 前端技术:使用 HTML、CSS 和 JavaScript 进行前端页面的开发,结合 Vue.js 前端框架,构建响应式、交互性强的用户界面。Vue.js 具有简洁的语法和丰富的组件库,能够方便地实现数据绑定、组件化开发等功能,提高前端开发的效率和代码的可维护性。此外,还可以使用 Element UI 等 UI 组件库,快速搭建美观、易用的界面。

数据库设计

根据在线图书商城的业务需求,设计以下主要的数据表:

  • 用户表(user):存储用户的基本信息,如用户 ID(主键,自增长)、用户名、密码、真实姓名、性别、年龄、联系方式、邮箱、地址等。
    CREATE TABLE user (
    
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    
    username VARCHAR(50) NOT NULL UNIQUE,
    
    password VARCHAR(100) NOT NULL,
    
    real_name VARCHAR(50),
    
    gender CHAR(1),
    
    age INT,
    
    phone VARCHAR(20),
    
    email VARCHAR(100),
    
    address VARCHAR(200)
    
    );

  • 图书表(book):记录图书的详细信息,包括图书 ID(主键,自增长)、书名、作者、出版社、出版日期、ISBN 号、价格、库存数量、封面图片路径、内容简介等。
    CREATE TABLE book (
    
    book_id INT AUTO_INCREMENT PRIMARY KEY,
    
    title VARCHAR(200) NOT NULL,
    
    author VARCHAR(100),
    
    publisher VARCHAR(100),
    
    publish_date DATE,
    
    isbn VARCHAR(20) UNIQUE,
    
    price DECIMAL(10, 2) NOT NULL,
    
    stock INT NOT NULL,
    
    cover_image VARCHAR(200),
    
    description TEXT
    
    );

  • 购物车表(cart):关联用户和图书,记录用户购物车中的图书信息,包括购物车 ID(主键,自增长)、用户 ID(外键,关联 user 表的 user_id)、图书 ID(外键,关联 book 表的 book_id)、购买数量等。
    CREATE TABLE cart (
    
    cart_id INT AUTO_INCREMENT PRIMARY KEY,
    
    user_id INT,
    
    book_id INT,
    
    quantity INT NOT NULL,
    
    FOREIGN KEY (user_id) REFERENCES user(user_id),
    
    FOREIGN KEY (book_id) REFERENCES book(book_id)
    
    );

  • 订单表(order):存储订单的基本信息,如订单 ID(主键,自增长)、用户 ID(外键,关联 user 表的 user_id)、订单编号(唯一)、下单时间、订单状态(如待付款、待发货、已发货、已完成等)、收货地址、收货人姓名、联系电话等。
    CREATE TABLE `order` (
    
    order_id INT AUTO_INCREMENT PRIMARY KEY,
    
    user_id INT,
    
    order_number VARCHAR(50) UNIQUE NOT NULL,
    
    order_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    order_status VARCHAR(20) NOT NULL,
    
    shipping_address VARCHAR(200),
    
    recipient_name VARCHAR(50),
    
    recipient_phone VARCHAR(20),
    
    FOREIGN KEY (user_id) REFERENCES user(user_id)
    
    );

  • 订单详情表(order_item):记录订单中具体购买的图书信息,包括订单详情 ID(主键,自增长)、订单 ID(外键,关联 order 表的 order_id)、图书 ID(外键,关联 book 表的 book_id)、购买数量、单价等。
    CREATE TABLE order_item (
    
    order_item_id INT AUTO_INCREMENT PRIMARY KEY,
    
    order_id INT,
    
    book_id INT,
    
    quantity INT NOT NULL,
    
    unit_price DECIMAL(10, 2) NOT NULL,
    
    FOREIGN KEY (order_id) REFERENCES `order`(order_id),
    
    FOREIGN KEY (book_id) REFERENCES book(book_id)
    
    );

模块划分

基于 MVC 架构模式,将系统划分为以下几个主要模块:

  • 控制器层(Controller):负责接收用户的请求,解析请求参数,并调用相应的服务层方法处理业务逻辑。在 Spring Boot 中,使用@Controller或@RestController注解标识控制器类,通过@RequestMapping等注解映射请求路径。例如,创建一个BookController类来处理与图书相关的请求:
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.web.bind.annotation.GetMapping;
    
    import org.springframework.web.bind.annotation.PathVariable;
    
    import org.springframework.web.bind.annotation.RestController;
    
    import com.example.demo.service.BookService;
    
    import com.example.demo.entity.Book;
    
    import java.util.List;
    
    @RestController
    
    public class BookController {
    
    @Autowired
    
    private BookService bookService;
    
    @GetMapping("/books")
    
    public List getAllBooks() {
    
    return bookService.getAllBooks();
    
    }
    
    @GetMapping("/books/{bookId}")
    
    public Book getBookById(@PathVariable Long bookId) {
    
    return bookService.getBookById(bookId);
    
    }
    
    }

  • 服务层(Service):实现业务逻辑,调用持久层接口进行数据的访问和操作。在服务层中,通常会对业务逻辑进行封装和处理,如数据的校验、事务的管理等。例如,创建一个BookService接口及其实现类BookServiceImpl:
    import com.example.demo.entity.Book;
    
    import com.example.demo.dao.BookMapper;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.stereotype.Service;
    
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    
    @Service
    
    @Transactional
    
    public class BookServiceImpl implements BookService {
    
    @Autowired
    
    private BookMapper bookMapper;
    
    @Override
    
    public List getAllBooks() {
    
    return bookMapper.getAllBooks();
    
    }
    
    @Override
    
    public Book getBookById(Long bookId) {
    
    return bookMapper.getBookById(bookId);
    
    }
    
    }

  • 持久层(Dao):负责与数据库进行交互,执行 SQL 语句,实现数据的增、删、改、查操作。在 MyBatis 中,通过编写 Mapper 接口和映射文件来实现持久层的功能。例如,创建一个BookMapper接口及其对应的映射文件BookMapper.xml:
    import com.example.demo.entity.Book;
    
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    @Mapper
    
    public interface BookMapper {
    
    List getAllBooks();
    
    Book getBookById(Long bookId);
    
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    

  • 实体层(Entity):定义与数据库表对应的 Java 实体类,通过注解或 XML 配置来实现对象与表的映射关系。例如,创建一个Book实体类:
    import lombok.Data;
    
    @Data
    
    public class Book {
    
    private Long bookId;
    
    private String title;
    
    private String author;
    
    private String publisher;
    
    private String publishDate;
    
    private String isbn;
    
    private Double price;
    
    private Integer stock;
    
    private String coverImage;
    
    private String description;
    
    }

(三)编码实现

按照上述设计方案,逐步实现各个功能模块。下面以用户注册和图书添加功能为例,展示关键代码和实现思路。

用户注册功能

  • 控制器层:在UserController中接收用户注册请求,获取请求参数(如用户名、密码、邮箱等),调用服务层的注册方法进行处理。
    import com.example.demo.service.UserService;
    
    import com.example.demo.entity.User;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.web.bind.annotation.PostMapping;
    
    import org.springframework.web.bind.annotation.RequestBody;
    
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    
    public class UserController {
    
    @Autowired
    
    private UserService userService;
    
    @PostMapping("/register")
    
    public String registerUser(@RequestBody User user) {
    
    try {
    
    userService.registerUser(user);
    
    return "注册成功";
    
    } catch (Exception e) {
    
    return "注册失败:" + e.getMessage();
    
    }
    
    }
    
    }

  • 服务层:在UserService接口及其实现类UserServiceImpl中实现用户注册的业务逻辑,包括对用户输入数据的校验(如用户名是否已存在、密码强度是否符合要求等),以及将用户信息保存到数据库中。
    import com.example.demo.dao.UserMapper;
    
    import com.example.demo.entity.User;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.stereotype.Service;
    
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    
    @Transactional
    
    public class UserServiceImpl implements UserService {
    
    @Autowired
    
    private UserMapper userMapper;
    
    @Override
    
    public void registerUser(User user) {
    
    // 校验用户名是否已存在
    
    if (userMapper.getUserByUsername(user.getUsername())!= null) {
    
    throw new RuntimeException("用户名已存在");
    
    }
    
    // 保存用户信息到数据库
    
    userMapper.insertUser(user);
    
    }
    
    }

  • 持久层:在UserMapper接口及其映射文件UserMapper.xml中编写 SQL 语句,实现用户信息的插入操作。
    import com.example.demo.entity.User;
    
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    
    public interface UserMapper {
    
    User getUserByUsername(String username);
    
    void insertUser(User user);
    
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})
    
    
    
    

图书添加功能

  • 控制器层:在BookController中接收图书添加请求,获取请求参数(如书名、作者、出版社等),调用服务层的添加方法进行处理。
    import com.example.demo.service.BookService;
    
    import com.example.demo.entity.Book;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.web.bind.annotation.PostMapping;
    
    import org.springframework.web.bind.annotation.RequestBody;
    
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    
    public class BookController {
    
    @Autowired
    
    private BookService bookService;
    
    @PostMapping("/books")
    
    public String addBook(@RequestBody Book book) {
    
    try {
    
    bookService.addBook(book);
    
    return "图书添加成功";
    
    } catch (Exception e) {
    
    return "图书添加失败:" + e.getMessage();
    
    }
    
    }
    
    }

  • 服务层:在BookService接口及其实现类BookServiceImpl中实现图书添加的业务逻辑,包括对图书信息的校验(如书名是否为空、价格是否合理等),以及将图书信息保存到数据库中。
    import com.example.demo.dao.BookMapper;
    
    import com.example.demo.entity.Book;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.stereotype.Service;
    
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    
    @Transactional
    
    public class BookServiceImpl implements BookService {
    
    @Autowired
    
    private BookMapper bookMapper;
    
    @Override
    
    public void addBook(Book book) {
    
    // 校验图书信息
    
    if (book.getTitle() == null || book.getTitle().isEmpty()) {
    
    throw new RuntimeException("书名不能为空");
    
    }
    
    if (book.getPrice() <= 0) {
    
    throw new RuntimeException("价格必须大于0");
    
    }
    
    // 保存图书信息到数据库
    
    bookMapper.insertBook(book);
    
    }
    
    }

  • 持久层:在BookMapper接口及其映射文件BookMapper.xml中编写 SQL 语句,实现图书信息的插入操作。
    import com.example.demo.entity.Book;
    
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    
    public interface BookMapper {
    
    void insertBook(Book book);
    
    }
    
    
    
    
    
    
    
    
    
    
    
    INSERT INTO book (title, author, publisher, publish_date, isbn, price, stock, cover_image, description)
    
    VALUES (#{title}, #{author}, #{publisher}, #{publishDate}, #{isbn}, #{price}, #{stock}, #{coverImage}, #{description})
    
    
    
    

(四)测试与部署

在完成编码实现后,需要对项目进行全面的测试,确保系统的功能正确性和稳定性。同时,将项目部署到服务器上,使其能够对外提供服务。

测试

  • 单元测试:使用 JUnit 等测试框架对各个功能模块的方法进行单元测试,验证方法的输入和输出是否符合预期。例如,对UserService中的registerUser方法进行单元测试:
 
   

import com.example.demo.entity.User;

import com.example.demo.service.User

## 常见问题与解决方案

![need_search_image_by_title]()

在Java Web开发过程中,开发者常常会遭遇各种棘手的问题,这些问题不仅影响开发进度,还可能对应用的性能和稳定性产生负面影响。以下是一些常见问题及对应的解决方案:

### (一)乱码问题

在Java Web开发中,乱码问题是最为常见的困扰之一。其产生的原因主要是由于不同系统或组件之间的字符编码不一致。例如,前端页面使用UTF - 8编码发送数据,而服务器端使用ISO - 8859 - 1编码接收和处理,就会导致乱码现象。常见的乱码场景包括GET和POST请求参数传输时的乱码,以及服务器响应数据在浏览器中显示的乱码 。

解决乱码问题可以采用以下多种方法:

- **设置请求和响应编码**:在Servlet中,通过`request.setCharacterEncoding("UTF-8");`和`response.setCharacterEncoding("UTF-8");`设置请求和响应的编码格式,确保数据在传输过程中编码一致。对于响应内容类型,还需设置`response.setContentType("text/html;charset=UTF-8");`,以告知浏览器正确的编码方式。

```java

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

request.setCharacterEncoding("UTF-8");

response.setCharacterEncoding("UTF-8");

response.setContentType("text/html;charset=UTF-8");

// 业务逻辑代码

}

  • 配置 Tomcat 编码:修改 Tomcat 安装目录下的server.xml文件,在标签中添加URIEncoding="UTF-8"属性,这样可以统一处理 GET 请求参数的编码问题。例如:

  • 使用过滤器:创建一个编码过滤器,实现Filter接口,在doFilter方法中对所有请求进行统一的编码设置。同时,利用动态代理对HttpServletRequest的getParameter方法进行扩展,使其能够处理 GET 请求的乱码问题。然后在web.xml文件中配置该过滤器。
    import javax.servlet.*;
    
    import javax.servlet.annotation.WebFilter;
    
    import javax.servlet.http.HttpServletRequest;
    
    import javax.servlet.http.HttpServletRequestWrapper;
    
    import java.io.IOException;
    
    import java.net.URLDecoder;
    
    import java.nio.charset.StandardCharsets;
    
    @WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
    
    public class EncodingFilter implements Filter {
    
    @Override
    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    
    HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
    
    @Override
    
    public String getParameter(String name) {
    
    String value = super.getParameter(name);
    
    if (value!= null) {
    
    try {
    
    value = URLDecoder.decode(value, StandardCharsets.UTF_8.name());
    
    } catch (Exception e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    return value;
    
    }
    
    };
    
    wrapper.setCharacterEncoding("UTF-8");
    
    servletResponse.setCharacterEncoding("UTF-8");
    
    filterChain.doFilter(wrapper, servletResponse);
    
    }
    
    @Override
    
    public void init(FilterConfig filterConfig) throws ServletException {
    
    // 初始化逻辑
    
    }
    
    @Override
    
    public void destroy() {
    
    // 销毁逻辑
    
    }
    
    }
    
    
    
    
    
    EncodingFilter
    
    EncodingFilter
    
    
    
    
    
    EncodingFilter
    
    /*
    
    

(二)依赖冲突

依赖冲突在使用 Maven 等构建工具管理项目依赖时经常出现。当多个依赖项依赖于同一个库的不同版本时,就会引发冲突。例如,项目中 A 依赖包需要commons - logging的 1.1 版本,而 B 依赖包需要commons - logging的 1.2 版本,这就可能导致在运行时出现类找不到或方法找不到的错误 。

解决依赖冲突可以采用以下方法:

  • 使用exclusions排除依赖:在pom.xml文件中,通过标签排除不需要的依赖版本。例如,如果要排除spring - core依赖的commons - logging,可以这样配置:
    
    
    org.springframework
    
    spring-core
    
    ${spring.version}
    
    
    
    
    
    commons-logging
    
    commons-logging
    
    
    
    
    
    

  • 使用dependencyManagement统一管理版本:在pom.xml文件的标签中,统一声明依赖的版本号,确保项目中所有依赖使用相同版本的库。例如:
    
    
    
    
    
    
    commons-logging
    
    commons-logging
    
    1.2
    
    
    
    
    
    

  • 使用工具分析依赖树:借助 IDEA 等开发工具提供的依赖树查看功能,分析项目的依赖关系,找出冲突的依赖项。在 IDEA 中,可以通过Maven Projects面板中的Show Dependencies按钮查看依赖树,冲突的依赖项会以红色线条标识,方便开发者定位和解决问题。

(三)HTTP 请求方法不支持

在开发过程中,有时会遇到 HTTP 请求方法不被支持的情况。例如,前端发送了一个 POST 请求,但后端的控制器方法只定义了处理 GET 请求,就会导致405 Method Not Allowed错误 。

解决这个问题的方法是确保后端控制器方法能够正确处理前端发送的请求方法。在 Spring MVC 中,可以使用@RequestMapping注解的method属性来指定支持的请求方法,或者使用更为具体的@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等注解。例如:

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping("/user")

public class UserController {

@PostMapping("/register")

public String registerUser() {

// 处理用户注册逻辑

return "注册成功";

}

}

在上述代码中,@PostMapping("/register")明确表示该方法只处理/user/register路径的 POST 请求。如果前端发送的是 GET 请求到该路径,就会返回405 Method Not Allowed错误。通过这种方式,可以清晰地定义控制器方法对不同请求方法的支持,避免因请求方法不匹配而导致的错误。

 


你可能感兴趣的:(java,web)