1,JavaWeb后端
经过前面的学习,现在终于可以正式进入到后端的学习当中,不过,我们还是需要再系统地讲解一下HTTP通信基础知识,它是我们学习JavaWeb的基础知识,我们之前已经学习过TCP通信,而HTTP实际上是基于TCP协议之上的应用层协议,因此理解它并不难理解。
打好基础是关键!为什么要去花费时间来讲解计算机网络基础,我们学习一门技术,如果仅仅是知道如何使用却不知道其原理,那么就成了彻头彻尾的“码农”,只知道搬运代码实现功能,却不知道这行代码的执行流程,在遇到一些问题的时候就不知道如何解决,无论是知识层面还是应用层面都得不到提升。
无论怎么样,我们都要明确,我们学习JavaWeb的最终目的是为了搭建一个网站,并且让用户能访问我们的网站并在我们的网站上做一些事情。
2,计算机网络基础
在计算机网络(谢希仁 第七版 第264页)中,是这样描述万维网的:
万维网(World Wide Web)并非是某种特殊的计算机网络,
万维网是一个大规模的联机式信息储藏所,英文简称Web,万维网用链接的方法,
能够非常方便地从互联网上的一个站点访问另一个站点,
从而主动地按需求获取丰富的信息。
这句话说的非常官方,但是也蕴藏着许多的信息,首先它指明,我们的互联网上存在许许多多的服务器,而我们通过访问这些服务器就能快速获取服务器为我们提供的信息(比如打开百度就能展示搜索、打开小破站能刷视频、打开微博能查看实时热点)而这些服务器就是由不同的公司在运营。
其次,我们通过浏览器,只需要输入对应的网址或是点击页面中的一个链接,就能够快速地跳转到另一个页面,从而按我们的意愿来访问服务器。
而书中是这样描述万维网的工作方式:
万维网以客户服务器的方式工作,浏览器就是安装在用户主机上的万维网客户程序,
万维网文档所驻留的主机则运行服务器程序,因此这台主机也称为万维网服务器。
客户程序向服务器程序发出请求,服务器程序向客户程序送回客户所要的万维网文档,
在一个客户程序主窗口上显示出的万维网文档称为页面。
上面提到的客户程序其实就是我们电脑上安装的浏览器,而服务端就是我们即将要去学习的Web服务器,也就是说,我们要明白如何搭建一个Web服务器并向用户发送我们提供的Web页面,在浏览器中显示的,一般就是HTML文档被解析后的样子。
那么,我们的服务器可能不止一个页面,可能会有很多个页面,那么客户端如何知道该去访问哪个服务器的哪个页面呢?这个时候就需要用到URL统一资源定位符。互联网上所有的资源,都有一个唯一确定的URL,比如http://www.baidu.com
URL的格式为:
<协议>://<主机>:<端口>/<路径>
协议是指采用什么协议来访问服务器,不同的协议决定了服务器返回信息的格式,我们一般使用HTTP协议。
主机可以是一个域名,也可以是一个IP地址(实际上域名最后会被解析为IP地址进行访问)
端口是当前服务器上Web应用程序开启的端口,我们前面学习TCP通信的时候已经介绍过了,HTTP协议默认使用80端口,因此有时候可以省略。
路径就是我们希望去访问此服务器上的某个文件,不同的路径代表访问不同的资源。
3,认识Tomcat服务器
Tomcat(汤姆猫)就是一个典型的Web应用服务器软件,通过运行Tomcat服务器,我们就可以快速部署我们的Web项目,并交由Tomcat进行管理,我们只需要直接通过浏览器访问我们的项目即可。
那么首先,我们需要进行一个简单的环境搭建,我们需要在Tomcat官网下载最新的Tomcat服务端程序:https://tomcat.apache.org/download-10.cgi(下载速度可能有点慢)
下载:64-bit Windows zip
下载完成后,解压,并放入桌面,接下来需要配置一下环境变量,打开高级系统设置,打开环境变量,添加一个新的系统变量,变量名称为JRE_HOME,填写JDK的安装目录+/jre,比如Zulujdk默认就是:C:\Program Files\Zulu\zulu-8\jre
设置完成后,我们进入tomcat文件夹bin目录下,并在当前位置打开CMD窗口,将startup.sh拖入窗口按回车运行,如果环境变量配置有误,会提示,若没问题,服务器则正常启动。
如果出现乱码,说明编码格式配置有问题,我们修改一下服务器的配置文件,打开conf文件夹,找到logging.properties文件,这就是日志的配置文件(我们在前面已经给大家讲解过了)将ConsoleHandler的默认编码格式修改为GBK编码格式:
java.util.logging.ConsoleHandler.encoding = GBK
现在重新启动服务器,就可以正常显示中文了。
服务器启动成功之后,不要关闭,我们打开浏览器,在浏览器中访问:http://localhost:8080/,Tomcat服务器默认是使用8080端口(可以在配置文件中修改),访问成功说明我们的Tomcat环境已经部署成功了。
整个Tomcat目录下,我们已经认识了bin目录(所有可执行文件,包括启动和关闭服务器的脚本)以及conf目录(服务器配置文件目录),那么我们接着来看其他的文件夹:
● lib目录:Tomcat服务端运行的一些依赖,不用关心。
● logs目录:所有的日志信息都在这里。
● temp目录:存放运行时产生的一些临时文件,不用关心。
● work目录:工作目录,Tomcat会将jsp文件转换为java文件(我们后面会讲到,这里暂时不提及)
● webapp目录:所有的Web项目都在这里,每个文件夹都是一个Web应用程序:
我们发现,官方已经给我们预设了一些项目了,访问后默认使用的项目为ROOT项目,也就是我们默认打开的网站。
我们也可以访问example项目,只需要在后面填写路径即可:http://localhost:8080/examples/,或是docs项目(这个是Tomcat的一些文档)http://localhost:8080/docs/
Tomcat还自带管理页面,我们打开:http://localhost:8080/manager,提示需要用户名和密码,由于不知道是什么,我们先点击取消,页面中出现如下内容:
You are not authorized to view this page. If you have not changed any configuration files, please examine the file conf/tomcat-users.xml in your installation. That file must contain the credentials to let you use this webapp.
For example, to add the manager-gui role to a user named tomcat with a password of s3cret, add the following to the config file listed above.
Note that for Tomcat 7 onwards, the roles required to use the manager application were changed from the single manager role to the following four roles. You will need to assign the role(s) required for the functionality you wish to access.
● manager-gui - allows access to the HTML GUI and the status pages
● manager-script - allows access to the text interface and the status pages
● manager-jmx - allows access to the JMX proxy and the status pages
● manager-status - allows access to the status pages only
The HTML interface is protected against CSRF but the text and JMX interfaces are not. To maintain the CSRF protection:
● Users with the manager-gui role should not be granted either the manager-script or manager-jmx roles.
● If the text or jmx interfaces are accessed through a browser (e.g. for testing since these interfaces are intended for tools not humans) then the browser must be closed afterwards to terminate the session.
For more information - please see the Manager App How-To.
<role rolename="manager-gui"/>
<user username="tomcat" password="s3cret" roles="manager-gui"/>
现在我们按照上面的提示,去配置文件中进行修改:
<role rolename="manager-gui"/>
<user username="admin" password="admin" roles="manager-gui"/>
现在再次打开管理页面,已经可以成功使用此用户进行登陆了。登录后,展示给我们的是一个图形化界面,我们可以快速预览当前服务器的一些信息,包括已经在运行的Web应用程序,甚至还可以查看当前的Web应用程序有没有出现内存泄露。
同样的,还有一个虚拟主机管理页面,用于一台主机搭建多个Web站点,一般情况下使用不到,这里就不做演示了。
4,使用Maven创建Web项目
引入maven
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study</groupId>
<artifactId>javaweb_maven_demo01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式-->
<packaging>war</packaging>
</project>
5,Servlet
前面我们已经完成了基本的环境搭建,那么现在我们就可以开始来了解我们的第一个重要类——Servlet。
它是Java EE的一个标准,大部分的Web服务器都支持此标准,包括Tomcat,就像之前的JDBC一样,由官方定义了一系列接口,而具体实现由我们来编写,最后交给Web服务器(如Tomcat)来运行我们编写的Servlet。
那么,它能做什么呢?我们可以通过实现Servlet来进行动态网页响应,使用Servlet,不再是直接由Tomcat服务器发送我们编写好的静态网页内容(HTML文件),而是由我们通过Java代码进行动态拼接的结果,它能够很好地实现动态网页的返回。
当然,Servlet并不是专用于HTTP协议通信,也可以用于其他的通信,但是一般都是用于HTTP。
5.1 快速入门
引入maven
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
创建ServletDemo类并像控制台输出一句话
package com.study;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo implements Servlet {
public void init(ServletConfig config) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("111111");
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
修改xml
<web-app>
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>com.study.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
</web-app>
启动tomcat通过浏览器访问程序
http://localhost:8080/javaweb_maven_demo01_war/demo1
5.2 Servlet执行流程
5.3 Servlet生命周期
package com.study;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo2 implements Servlet {
/**
* servlet被创建时执行
* @param config
* @throws ServletException
*/
public void init(ServletConfig config) throws ServletException {
System.out.println("init...");
}
/**
* servlet被调用时执行
* @param req
* @param res
* @throws ServletException
* @throws IOException
*/
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("service...");
}
/**
* servlet被销毁时执行
*/
public void destroy() {
System.out.println("destroy...");
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
}
5.4 Servlet体系结构
package com.study;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo3 extends HttpServlet {
/**
* 处理get请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet");
super.doGet(req, resp);
}
/**
* 处理post请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
super.doPost(req, resp);
}
/**
* 处理put请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPut");
super.doPut(req, resp);
}
/**
* 处理Delete请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doDelete");
super.doDelete(req, resp);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="post">
<input name="username">
<input type="submit">
</form>
</body>
</html>
6,Request(请求)和Response(响应)
6.1 Request继承体系
6.2 Request获取请求数据
6.2.1 获取请求行
package com.study;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo3 extends HttpServlet {
/**
* 处理get请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
System.out.println("getMethod:"+req.getMethod());
System.out.println("getContextPath:"+req.getContextPath());
System.out.println("getContextPath:"+req.getContextPath());
System.out.println("getRequestURI:"+req.getRequestURI());
System.out.println("getQueryString(:"+req.getQueryString());
// System.out.println("doGet");
// String name = req.getParameter("name");
//
// resp.setHeader("content-type","text/html;charset=utf-8");
// resp.getWriter().write("<h1>"+ name +"欢迎你</h1>");
}
/**
* 处理post请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
// super.doPost(req, resp);
}
/**
* 处理put请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPut");
// super.doPut(req, resp);
}
/**
* 处理Delete请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doDelete");
// super.doDelete(req, resp);
}
}
6.2.2 获取请求头
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
System.out.println("getHeader:"+req.getHeader("Accept-Language"));
// System.out.println("doGet");
// String name = req.getParameter("name");
//
// resp.setHeader("content-type","text/html;charset=utf-8");
// resp.getWriter().write("<h1>"+ name +"欢迎你</h1>");
}
6.2.3 获取请求体
/**
* 处理post请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
//获取请求体
BufferedReader br = req.getReader();
String line = br.readLine();
System.out.println(line);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
6.3 Request通用方式获取请求数据
6.3.1 get请求接收数据
6.3.1.1 获取所有参数的map集合
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
//获取所有参数的map集合
Map<String, String[]> map =
req.getParameterMap();
for (String key : map.keySet()) {
System.out.println(key+":");
String[] values = map.get(key);
for (String value : values) {
System.out.print(value+" ");
}
System.out.println();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<input type="submit">
</form>
</body>
</html>
6.3.1.2 更具key获取参数值,数组
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
//更具key获取参数值,数组
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<input type="submit">
</form>
</body>
</html>
6.3.1.3 更具key获取单个参数值
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
//更具key获取单个参数值
String password = req.getParameter("password");
System.out.println(password);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<input type="submit">
</form>
</body>
</html>
6.3.2 post请求接收数据
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取所有参数的map集合
Map<String, String[]> map =
req.getParameterMap();
for (String key : map.keySet()) {
System.out.println(key+":");
String[] values = map.get(key);
for (String value : values) {
System.out.print(value+" ");
}
System.out.println();
}
System.out.println("==================");
//更具key获取参数值,数组
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
System.out.println("==================");
//更具key获取单个参数值
String password = req.getParameter("password");
System.out.println(password);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/javaweb_maven_demo01_war/demo3" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<input type="submit">
</form>
</body>
</html>
6.4 Request请求转发
package com.study;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
public class ServletDemo4 extends HttpServlet {
/**
* 处理get请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo4");
Object iphone = req.getAttribute("iphone");
System.out.println(iphone.toString());
}
/**
* 处理post请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
package com.study;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo5 extends HttpServlet {
/**
* 处理get请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo5");
req.setAttribute("iphone","18110229299");
//请求转发
req.getRequestDispatcher("/demo4").forward(req,resp);
}
/**
* 处理post请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
6.5 Response设置响应数据和重定向
6.5.1 Response设置响应数据
6.5.2 Response重定向
package com.study.response;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ResponseDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ResponseDemo01.....");
//重定向
//设置响应状态码
// resp.setStatus(302);
//设置响应头
// resp.setHeader("Location","/javaweb_maven_demo01/resp2");
// 动态获取虚拟目录
String contextPath = req.getContextPath();
//简化写法
resp.sendRedirect(contextPath+"/resp2");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
package com.study.response;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ResponseDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ResponseDemo02....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
6.5.3 Response响应字符数据
package com.study.response;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置中文编码
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
//设置头信息
// resp.setHeader("content-type","text/html");
writer.write("<h1>aaa,张三</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
6.5.4 Response响应字节数据
package com.study.response;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1,读取文件
FileInputStream fis =
new FileInputStream("E://Snipaste_2023-01-09_18-46-55.png");
// 2,获取response字节输出流
ServletOutputStream os = resp.getOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff)) != -1){
os.write(buff,0,len);
}
fis.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}