【Java Web02】WEB服务器内部实现技术揭秘

JAVA WEB内部实现技术揭秘

  • 一、XML
    • [1.1 xml简介](#1.1 xml简介)
    • [1.2 DOM4J解析](#1.2 DOM4J解析)
  • 二、Tomcat10
    • [2.1 Tomcat简介](#2.1 Tomcat简介)
    • [2.2 安装Tomcat](#2.2 安装Tomcat)
    • [2.3 WebApp的标准结构](#2.3 WebApp的标准结构)
    • [2.4 Tomcat部署项目的方式](#2.4 Tomcat部署项目的方式)
    • [2.5 IDEA关联Tomcat](#2.5 IDEA关联Tomcat)
    • [2.6 创建、构建、部署JAVA WEB工程](#2.6 创建、构建、部署JAVA WEB工程)
    • [2.7 IDEA使用Tomcat运行java web项目的原理](#2.7 IDEA使用Tomcat运行java web项目的原理)
  • 三、HTTP协议
    • [3.1 http简介](#3.1 http简介)
    • [3.2 HTTP请求和响应的报文格式](#3.2 HTTP请求和响应的报文格式)
      • [3.2.1 请求报文](#3.2.1 请求报文)
      • [3.2.2 响应报文](#3.2.2 响应报文)
    • [3.3 响应状态码](#3.3 响应状态码)
  • 四、Servlet
    • [4.1 servlet简介](#4.1 servlet简介)
    • [4.2 Hello,Servlet](#4.2 Hello,Servlet)
    • [4.3 注解方式配置servlet](#4.3 注解方式配置servlet)
    • [4.4 Servlet生命周期](#4.4 Servlet生命周期)
    • [4.5 ServletConfig的使用](#4.5 ServletConfig的使用)
    • [4.7 ServletContext](#4.7 ServletContext)
    • [4.8 HttpServletRequest](#4.8 HttpServletRequest)
    • [4.9 HttpServletResponse](#4.9 HttpServletResponse)
  • 五、请求转发和响应重定向
    • [5.1 什么是请求转发和响应重定向](#5.1 什么是请求转发和响应重定向)
    • [5.2 请求转发](#5.2 请求转发)
    • [5.3 响应重定向](#5.3 响应重定向)
  • 六、web乱码问题和路径问题总结
    • [6.1 乱码问题](#6.1 乱码问题)
      • [6.1.1 HTML乱码](#6.1.1 HTML乱码)
      • [6.1.2 Tomcat控制台乱码](#6.1.2 Tomcat控制台乱码)
      • [6.1.3 suot乱码](#6.1.3 suot乱码)
      • [6.1.4 Get请求乱码](#6.1.4 Get请求乱码)
      • [6.1.5 Post请求乱码](#6.1.5 Post请求乱码)
      • [6.1.6 响应乱码问题](#6.1.6 响应乱码问题)
    • [6.2 路径问题](#6.2 路径问题)
      • [6.2.1 前端相对路径问题](#6.2.1 前端相对路径问题)
      • [6.2.2 前端绝对路径问题](#6.2.2 前端绝对路径问题)
      • [6.2.3 响应重定向的路径问题](#6.2.3 响应重定向的路径问题)
      • [6.2.4 请求转发的路径问题](#6.2.4 请求转发的路径问题)
      • [6.2.5 不设置项目上下文路径](#6.2.5 不设置项目上下文路径)
  • 七、MVC架构模式
  • 八、日程管理第二期
    • [8.1 项目搭建](#8.1 项目搭建)
      • [8.1.1 数据库准备](#8.1.1 数据库准备)
      • [8.1.2 实体类](#8.1.2 实体类)
      • [8.1.3 工具类](#8.1.3 工具类)
      • [8.1.4 DAO](#8.1.4 DAO)

未来已来,GPT4o也上线啦,需要使用GPT镜像的可以私聊我哟,可试用,包稳定,官方价格打骨折。

接下来我们一起学习java web的内部实现技术,本篇我们将介绍web服务器中的所有重要知识点和难点,包括乱码问题的解决、路径问题的深入讲解,重定向、请求转发的深入对比...

一、XML

1.1 xml简介

简单来说,xml就是在java中做配置文件的。擅长处理复杂的层级结构。

回顾下常见的配置文件类型。

对比下porperties和xml

xml有如下注意事项。

比如下面就是一个约束为schema类型的xml文件。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>

1.2 DOM4J解析

使用DOM4J可以在java中进行xml解析。步骤如下。

自己导入下dom4j的jar包。

https://mvnrepository.com/artifact/org.dom4j/dom4j/2.1.4

xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<jdbc-config>
    <driver>com.mysql.cj.jdbc.Driver</driver>
    <url>jdbc:mysql://localhost:3306/mydatabase</url>
    <username>myuser</username>
    <password>mypassword</password>
</jdbc-config>

标记为资源目录

代码

java 复制代码
package com.wangzhou;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;

import java.io.InputStream;
import java.net.URL;

public class TestDom4j {

    @Test
    public void testRead() throws DocumentException {
        SAXReader reader = new SAXReader();
        InputStream resourceAsStream = TestDom4j.class.getClassLoader().getResourceAsStream("jdbc.xml");
        Document document = reader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        System.out.println(rootElement.getName());
        rootElement.elements().forEach(element -> {
            System.out.println("\t"+element.getName()+"="+element.getText());
        });

    }
}

运行结果如下。

二、Tomcat10

2.1 Tomcat简介

一句话说,光有jre,app跑不起来。Tomcat可以让我们在服务器上的app跑起来。如果比喻app是子弹,Tomcat就是一把枪。

2.2 安装Tomcat

官网:https://tomcat.apache.org/

解压到不带中文的路径下。

注意tomcat也是使用java语言进行开发的,因此需要匹配对应的jre运行环境。以Tomcat10为例,我们需要使用java11+。

你要是还没有jdk,自己下一个。

https://www.oracle.com/cn/java/technologies/downloads/#jdk22-windows

配置下系统环境变量。

把JAVA_Home添加到Path变量中。

确认下。

运行tomcat试试。

发现输出日志有中文乱码。问题不大,后面解决。

验证下是否启动成功,访问:http://localhost:8080/,看到可爱的小猫就说明成功了。

解决下日志中文乱码问题,找到日志配置文件。更改如下配置(windows窗口默认支持日志使用GBK):

复制代码
java.util.logging.ConsoleHandler.encoding = GBK

关掉重启,一切就正常了。

2.3 WebApp的标准结构

总结下。

补充下url组成部分和项目中资源的对应关系。

2.4 Tomcat部署项目的方式

2.5 IDEA关联Tomcat

IDEA和Tomcat有什么关系呢?看看下图。我们使用IDEA编辑源代码,这是一个JAVA WEB工程文件,再构建成一个可以用于部署的APP,将其部署到Tomcat。这都是用IDEA来做的。

具体的过程如下。

先建立tomcat和idea的关联。

2.6 创建、构建、部署JAVA WEB工程

新建项目,设置jdk配置。

为子模块增加tomcat 10依赖。

新建子模块。

转换为一个web项目。

现在项目结构会变成下面这样。index.jsp咱们不用,可以删除。

接下来把java web项目构建成为一个web app。

好了。

来部署下。

启动后,会自动打开浏览器(没有你就手动打开)

实际上,上面构建这一步,可以不用手动做。当你启动tomcat时,IDEA会自动先完成构建。

2.7 IDEA使用Tomcat运行java web项目的原理

这里仅仅了解下就好。

原来,为了不污染原来的tomcat,IDEA存了一个tomcat副本,只保存一些和项目运行相关的配置。tomcat本体去根据副本tomcat中寸的配置信息,运行web app。妙妙妙啊。

三、HTTP协议

3.1 http简介

学习http无非就是学习下报文的数据格式。

3.2 HTTP请求和响应的报文格式

3.2.1 请求报文

请求体:

3.2.2 响应报文

响应行和响应头

响应体

3.3 响应状态码

解释下。

405,比如只支持get方式访问,但你客户端通过post方式访问url。

500,你服务器的代码运行出错了,比如除0错误。

304,访问一个url获取图片后,浏览器缓存了这个图片资源,再次访问,没有去服务器拿,直接使用的缓存资源。

注意:

1、有时候浏览器的缓存会干扰你的实验。可以按住ctrl+shift+del清理缓存

2、使用下面图标可以热部署web项目,无需重启项目。

3、可以右键,查看页面源代码,确认浏览器是否更新了代码,避免缓存影响。

四、Servlet

4.1 servlet简介

动态资源和静态资源。

servlet是啥。

servlet运行流程如下。

4.2 Hello,Servlet

目标

添加tomcat依赖。

新建模块。变成web项目。

删除index.jsp,新建index.html。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="userServlet" method="get">
    用户名:<input type="text" name="username"><br>
    <input type="submit" value="校验">
</form>

</body>
</html>

编写UserServlet文件。

java 复制代码
package com.wangzhou.servlet;


import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class UserServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 获取请求参数username
        String username = req.getParameter("username");
        // 处理业务逻辑
        String info = "YES";
        if("wang".equals(username)) {
            info = "NO";// 响应数据
        }
        // 响应数据
        resp.getWriter().write(info);

    }
}

更改web.xml。

将部署项目设置下。改下项目url。

启动项目(建议使用debug模式,随时加断点)。

点击校验。

备注:

1、需要手工导入servlet的jar包吗?

2、一个servlet-name可以对应多个url-pattern

3、url-pattern可以模糊匹配

4.3 注解方式配置servlet

就为了建立url路径和servlet文件之间的映射,我们就写了下面这一段代码,太麻烦了

有什么简化的方法吗?有,上注解。

原来的代码先注释掉。

加一个@WebServlet("/userservlet")到UserServlet 上

java 复制代码
@WebServlet("/userservlet")
public class UserServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 获取请求参数username
        String username = req.getParameter("username");
        // 处理业务逻辑
        String info = "YES";
        if("wang".equals(username)) {
            info = "NO";// 响应数据
        }
        // 响应数据
        resp.getWriter().write(info);

    }
}

4.4 Servlet生命周期

你可以自己测试下。

通过测试,我们有如下总结。

Tomcat还有一个defaultservlet,我们访问静态资源,其实就是由defaultsevlet处理的。

使用spring mvc也提供了defaultservlet,会让tomcat中的defaultservlet失效。所以访问静态资源,可能会访问不到。需要重新配置使其生效。具体内容这里先留个悬念。

4.5 ServletConfig的使用

创建项目servlet03,重复上面两节内容,转为java web项目。修改配置文件。

当然,你也可以通过注解进行配置。

如下图,在项目启动后,tomcat会拿到config信息。

因此,我们可以通过servletConfig去获取这些配置信息。

如果我们再配置一个servlet02,那么它的相关配置,会通过一个新的servletconfig对象去存储。

4.7 ServletContext

ServletContext是单例的。

编写配置信息。

测试。

servletcontext的其它api

4.8 HttpServletRequest

其实无非就是一些API。

获取行相关的api。

URL与URI有什么区别?

再来。

为何会有客户端请求的端口号和应用所在容器的端口号的区别呢?因为可能有代理。

获取请求头相关api。

获取请求中键值对参数相关API。

先准备一个简单的表单。

测试代码如下。

4.9 HttpServletResponse

了解下咯。

五、请求转发和响应重定向

5.1 什么是请求转发和响应重定向

5.2 请求转发

测试代码。可以加进行测试。

5.3 响应重定向

测试代码。

六、web乱码问题和路径问题总结

6.1 乱码问题

6.1.1 HTML乱码

6.1.2 Tomcat控制台乱码

6.1.3 suot乱码

解决办法:设置虚拟机加载.class文件的字符集与编译时使用的字符集一致。

6.1.4 Get请求乱码

但是tomcat10解码默认是utf-8字符集。

因此下面tomcat控制台出现了乱码。

解决方法:更改tomcat配置

6.1.5 Post请求乱码

解决办法,设置请求体解码的字符集

6.1.6 响应乱码问题

解决。

6.2 路径问题

6.2.1 前端相对路径问题

情况1:目标资源所在路径与当前资源所在路径相同

项目结构和demo代码如下。

思考:查找资源的过程是怎么完成的?

情况2:目标资源所在路径与当前资源所在路径不相同,没有请求转发

情况2:目标资源所在路径与当前资源所在路径不相同,进行了请求转发

下面代码访问不了。因为用户无法直接访问view.html。必须经过请求转发。

分析过程。

正确写法:

正确的思考过程:资源的查找是客户端的事情,而不是服务端做,写相对路径从浏览器客户端的角度考虑!!

6.2.2 前端绝对路径问题

绝对路径怎么写?需要先找到项目的固定路径出发点。可以做测试。比如在index.html写如下代码。

请求index.html。打开开发者模式,查看网络中对于png的请求,点进去看看它请求图片的地址。

正确写法如下。

绝对路径也有缺点,因为项目上下文路径可以改变。

项目上下文路径一旦改变,引用了这个路径的地方全都要修改,如果有100个,你要改100次,有没有办法避免呢?有的。后续将会介绍。

6.2.3 响应重定向的路径问题

6.2.4 请求转发的路径问题

6.2.5 不设置项目上下文路径

在开发过程中,我们通常不设置项目上下文路径,因为一台服务器就部署一个app(如下图)。这样写绝对路径,就不用加上下文路径了,省心。

七、MVC架构模式

到底啥是MVC模式啊?通俗来说,就是你在写代码时,项目文件不能乱放,乱来,必须遵守一定的架构规则。

不理解?看图。

八、日程管理第二期

8.1 项目搭建

8.1.1 数据库准备

使用navicat,连接到你的数据库。创建一个新的数据库。

执行如下sql。

sql 复制代码
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `sys_schedule`;
CREATE TABLE `sys_schedule` (
  `sid` int NOT NULL AUTO_INCREMENT,
  `uid` int NULL DEFAULT NULL,
  `title` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `completed` int(1) NULL DEFAULT NULL,
  PRIMARY KEY(`sid`) USING BTREE 
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = 
utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE  `sys_user` (
  `uid` int NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `user_pwd` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY(`uid`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE 
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = 
utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO   `sys_user` VALUES(1,'zhangsan','sxdfcgvhbjksdfghjk');
INSERT INTO   `sys_user` VALUES(2,'lisi','sxdfcgvhbjksdfghjk');
SET FOREIGN_KEY_CHECKS = 1;

创建项目schedule_system,添加tomcat依赖,添加java web framework组件支持。

其实,你也可以使用IDEA连接数据库,无需再使用别的数据库工具。

点击1 of 5选择需要展示的库。

8.1.2 实体类

检查是否安装了Lombok插件。

Enable Annotation Processing

创建实体类。

java 复制代码
@AllArgsConstructor
public class SysUser implements Serializable {
    private Integer uid;
    private String username;
    private String userPwd;
}

下一个

java 复制代码
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SysSchedule {
    private Integer sid;
    private Integer uid;
    private String title;
    private Integer completed;
}

8.1.3 工具类

导入druid的jar包。

编写JDBC连接工具类。

java 复制代码
package com.wangzhou.schedule.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class JDBCUtil {
    private static ThreadLocal<Connection> threadLocal =new ThreadLocal<>();

    private static DataSource dataSource;
    // 初始化连接池
    static{
        // 可以帮助我们读取.properties配置文件
        Properties properties =new Properties();
        InputStream resourceAsStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }
    /*1 向外提供连接池的方法*/
    public static DataSource getDataSource(){
        return dataSource;
    }

    /*2 向外提供连接的方法*/
    public static Connection getConnection(){
        Connection connection = threadLocal.get();
        if (null == connection) {
            try {
                connection = dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            threadLocal.set(connection);
        }

        return connection;
    }


    /*定义一个归还连接的方法 (解除和ThreadLocal之间的关联关系) */
    public static void releaseConnection(){
        Connection connection = threadLocal.get();
        if (null != connection) {
            threadLocal.remove();
            // 把连接设置回自动提交的连接
            try {
                connection.setAutoCommit(true);
                // 自动归还到连接池
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

编写jdbc.properties。

properties 复制代码
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/schedule_system
username=root
password=root
initialSize=5
maxActive=10
maxWait=1000

8.1.4 DAO

java 复制代码
  public <T> T baseQueryObject(Class<T> clazz, String sql, Object ... args) {
        T t = null;
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        int rows = 0;
        try {
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }

            // 执行 查询
            resultSet = preparedStatement.executeQuery();
            if (resultSet.next()) {
                t = (T) resultSet.getObject(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != resultSet) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }
            JDBCUtil.releaseConnection();
        }
        return t;
    }
相关推荐
gis开发之家1 小时前
日志怎么打才专业?Spring Boot 4 日志体系与 Logback 配置(生产级实战)
java·spring boot·后端·logback
拿本唠嗑AI研究1 小时前
现代前端架构爬虫指南:从静态页面到 React/Vue 单页应用
前端·爬虫·架构
SaaS发言人1 小时前
渠道数字化解决方案横评:谁更懂快消
java·大数据·人工智能
JL151 小时前
Java并发编程面试全攻略-从synchronized到AQS底层原理
java·开发语言·面试·并发编程
张宇Joaquin1 小时前
高性能计算之MPI——集合通信漫谈
服务器·microsoft·asp.net
ZC跨境爬虫1 小时前
LeetCode 119. 杨辉三角 II(原地更新优化详解 + Java Python 实现)
java·python·leetcode
独孤九剑打醒他1 小时前
【原创开源】双层Master‑Worker软硬协同调度架构:从根源解决分布式数据一致性难题
前端·嵌入式硬件·架构·前端框架·硬件工程
程序员黑豆7 小时前
Java包装类:基本类型与对象的桥梁
java·前端·ai编程