JavaWeb入门到实战
学习视频来自B站,感谢狂神的分享:B站视频地址
1. 基本概念
Web
- web(World Wide Web)即全球广域网,也称万维网,它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。是建立在Internet上的一种网络服务,为浏览者在Internet上查找和浏览信息提供了图形化的、易于访问的直观界面,其中的文档及超级链接将Internet上的信息节点组织成一个互为关联的网状结构。
- 静态网页 是指没有后台数据库、不含程序和不可交互的网页。纯粹HTML格式的网页,文件扩展名是.htm、.html,可以包含文本、图像、声音、FLASH动画、客户端脚本和ActiveX控件及JAVA小程序等。
提供给所有人看的,数据始终不会发生变化!
- 动态网页 是指基本的html语法规范与Java、VB、VC等 高级程序设计语言、数据库编程 等多种技术的融合,以期实现对网站内容和风格的高效、动态和交互式的管理。
’提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!
Web应用程序
- 是一种可以通过Web访问的应用程序,用户只需要有浏览器即可访问。
- 应用程序有两种模式C/S、B/S。WEB应用程序一般是B/S模式,是由完成特定任务的各种Web组件 (web components) 构成的并通过Web将服务展示给外界。在实际应用中,Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。
- web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理;
Web编程语言
WEB编程语言,分为WEB静态语言和WEB动态语言。WEB静态语言 就是通常所见到的超文本标记语言 ,WEB动态语言 主要是ASP,PHP,JAVASCRIPT,JAVA,CGI等计算机脚本语言。
2. Web服务器
技术讲解
ASP(Active Server Pages)是 Microsoft 公司开发的服务器端脚本环境,可以包含文本、HTML(包括相关的客户端脚本)和com组件调用。用C#开发,运营在IIS服务器上。
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。 狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般指后者。
jSP/Servlet
sun公司主推的B/S架构,基于Java语言的,语法像ASP:ASP->JSP,加强市场强度。
web服务器
Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序。可以处理浏览器等Web客户端的请求并返回相应响应。目前最主流的三个Web服务器是Apache、 Nginx 、IIS。
IIS(Internet Information Services)互联网信息服务,是由微软公司提供的基于运行Microsoft Windows的互联网基本服务。
Nginx 是一款轻量级的 Web 服务器、反向代理服务器 及 电子邮件(IMAP/POP3)代理服务器,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。
Tomcat 是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,是一个开放源代码的轻量级Web 应用服务器,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。因为Tomcat 技术先进、性能稳定,而且免费,因而成为比较流行的Web 应用服务器。
3. Tomcat
tomcat下载安装:
tomcat 官网:https://tomcat.apache.org/
tomcat 文件夹
tomcat 配置
conf 文件夹下 server.xml,记录了有关服务器的配置:
端口号:其中 port 是指服务器的端口号,默认 8080:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
服务器名称:name指定服务器名称,appBase指定web程序文件夹
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
注:如果修改上面的 name=“localhost”,则新修改的名称,需要配置在C:\Windows\System32\drivers\etc\host中,映射到本地地址:
域名是如何访问
检查本机的 C:\Windows\System32\drivers\etc\hosts 配置文件下有没有这个域名映射:
- 有:直接返回对应的ip地址
- 没有:去DNS服务器找,找到的话就返回,找不到就报错;

发布一个网站
将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了。网站应该有的结构:
--webapps :Tomcat服务器的web目录
-ROOT
-kuangstudy :网站的目录名
- WEB-INF
-classes : java程序
-lib:web应用所依赖的jar包
-web.xml :网站配置文件
- index.html 默认的首页
- static
-css
-style.css
-js
-img
-.....
4. Http
概念
Http:(Hyper Text Transfer Protocol,超文本传输协议)是一个简单的 请求-响应协议 ,默认端口是80,它通常 运行在TCP之上 。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。
两个时代
Http/1.0:
- HTTP/1.0最早在 1996 年使用于网页中,只是使用在简单的网页和网络请求上
- 默认使用短连接,每次请求都要重新建立一次连接。HTTP 是基于TCP/IP协议的,每一次建立或者断开连接都需要三次握手四次挥手的开销,如果每次请求都要这样的话,开销会比较大。
- HTTP 1.0 中,主要使用 header 头里的
If-Modified-Since、Expires来做为缓存判断的标准 - 存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象传送了过来,并且不支持断点续传功能
Http/1.1:客户端可以与web服务器连接后,可以获得多个web资源。
- HTTP/1.1 从 1999 年才广泛应用于现在的各大浏览器网络请求中,是当前使用最广泛的 HTTP 协议
- 默认使用长连接 ,默认开启
Connection: keep-alive。持续连接有非流水线方式和流水线方式 。流水线方式 是客户在收到HTTP的响应报文之前就能接着发送新的请求报文。非流水线方式 是客户在收到前一个响应后才能发送下一个请求。 - 在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- 引入了更多的缓存控制策略,如
Entity tag,If-Unmodified-Since,If-Match, If-None-Match等 - 在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样方便开发者自由的选择,以便于充分利用带宽和连接。
Http 请求
请求百度地址
Request URL:https://www.baidu.com/ --请求地址
Request Method:GET -- get方法/post方法
Status Code:200 OK -- 状态码:200
Remote Address:14.215.177.39:443 --服务器地址和端口
1、请求行
- 请求行中的 请求方式 :Request Method:GET
- 请求方式:Get,Post,HEAD,DELETE,PUT,TRACT.…
- get:请求能够携带的参数较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
- post:请求能够携带的 参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。
2、请求头 Request Headers
Accept: --代表发送端(浏览器)希望接受的数据类型
Accept-Encoding:gzip, deflate, br --浏览器支持的内容编码格式列表
Accept-Language:zh-CN,zh;q=0.9 --表示浏览器所支持的语言类型
Cache-Control: --缓存控制
Connection: --请求完成是断开还是保持连接
HOST:主机..../.
Http 响应
响应头 Response Headers
Cache-Control: private --缓存控制
Connection: keep-alive --连接是否保持
Content-Encoding: gzip --服务端选定的编码信息
Content-Type: text/html;charset=utf-8 --类型
面试题:
在浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?
5. Maven
5.1 安装及配置
maven依赖仓库:https://mvnrepository.com/
Maven官网:https://maven.apache.org/index.html
maven配置文件详解:https://www.jianshu.com/p/c2e0d61815a1

maven 概念
Apache Maven是一个(特别是Java编程)项目管理及自动构建工具,由Apache软件基金会所提供。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。
配置环境变量
M2_HOME : maven目录下的bin目录
MAVEN_HOME : maven的目录
系统path变量中配置: %MAVEN_HOME%\bin
配置镜像 (国内建议使用,阿里云的镜像)
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
配置本地maven仓库
如上图所示,在setting.xml中,使用 localRepository 配置本地仓库路径
<localRepository>D:\...\apache-maven-3.6.2\maven-repo</localRepository>
5.2 创建maven项目
打开idea,使用模板 创建一个maven项目
1、创建maven项目
2、设置项目的 G A V
3、选择项目的maven配置
4、构建成功
注意:idea中的maven配置:
手动创建maven项目
上面第一步,不要勾选模板,后续步骤相同:
5.3 标记项目文件夹
项目中的文件夹类别,可以通过以下两种方式标记:
右击文件夹 —》 Mark Directory as (选择文件夹的种类)
file —》 project structure:
5.4 配置tomcat


5.5 pom.xml
<!-- maven版本 和 头文件 -->
<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>
<!-- 配置maven的 GAV-->
<groupId>com.kuang</groupId>
<artifactId>simple_javaWeb01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--packaging:项目打包的方式:
jar: java 应用
war: javaweb应用-->
<packaging>war</packaging>
<!--配置信息-->
<properties>
<!--项目的默认构建编码-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--编码版本-->
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!-- 项目依赖 -->
<dependencies>
<!-- 依赖的具体jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
<build>
<!--为防止资源和配置文件存放不规范,导致项目报错出现异常,需进行如下配置-->
<resources>
<!-- src/main/java文件夹下,可以存放 *.properties、*.xml 文件,不用过滤 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</bulid>
5.6 注意事项
项目中的 web.xml 头文件,可以个换成 tomcat 的 web.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"
metadata-complete="true">
</web-app>
------------------------------------------
<!-- 注:由于idea版本问题,可能导致字体报红,无法自动提示。下面是3.1版本: -->
<?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_3_1.xsd"
version="3.1"
metadata-complete="true">
</web-app>
6. Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器。用Java编写的服务器端程序,可以生成动态Web内容。
SUN公司提供了一个接口:Servlet。平常所说的Servlet是指任何实现了这个接口的类。
6.1 第一个servlet程序
sun公司有两个默认的servlet实现类:HttpServlet、GenericServlet
1、建立一个普通的maven项目,删除src目录。以后就在这个项目里面建立Moudel;这个空的工程就是Maven主工程。
2、关于Maven父子工程的理解: 父项目中的 jar包, 子项目可以直接使用
<!-- 父项目中会有:-->
<modules>
<module>servlet-01</module>
</modules>
<!-- 子项目中会有: -->
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.zty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

3、在主工程的pom.xml,添加Servlet相关依赖。可在 https://mvnrepository.com/ 中查找最新版本
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
</dependency>
4、编写一个普通类,实现servlet接口,这里我们直接继承HttpServlet

package com.kuang;
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 HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>你好!</h1>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
5、注册Servlet,指明请求路径 (头文件替换成 tomcat中web.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"
metadata-complete="true">
<display-name>Welcome to Tomcat</display-name>
<!-- 注册servlet:名称和类路径-->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.kuang.HelloServlet</servlet-class>
<!-- 可以给servlet添加默认参数 -->
<init-param>
<param-name>myName</param-name>
<param-value>xinan</param-value>
</init-param>
</servlet>
<!-- 一个servlet对应一个Mapping:映射-->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name> <!--servlet名称,与上面对应-->
<url-pattern>/hello</url-pattern> <!-- 请求路径-->
</servlet-mapping>
</web-app>
6.2 Servlet原理

6.3 注册servlet
1、一个servlet可以指定一个映射路径
<!-- 一个servlet对应一个Mapping:映射-->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern> <!-- 请求路径-->
</servlet-mapping>
2、一个servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
3、一个servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
注意: 如下配置,不会默认进入index.jsp页面(不推荐使用)
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
4、指定前缀或后缀
<!-- 可以自定义后缀,实现请求映射: *.do *.qinjiang 都可以
注意点:* 前面不能加项目映射的路径: /hello/*.do 是不可以的
但是/hello/hi.do是可以的
-->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
5、优先级问题:
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
<!--404-->
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.zty.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
6.4 ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;
1、共享数据
我在这个Servlet中保存的数据,可以在另外一个servlet中拿到;
// ---------------创建第一个servlet,放入name参数----------------
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getInitParameter() 初始化参数
//this.getServletConfig() Servlet配置
//this.getServletContext() Servlet上下文
ServletContext context = this.getServletContext();
context.setAttribute("name","西南");
}
}
// --------------第二个servlet:取出name参数,并打印到页面--------------
public class ServletTwo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String name = (String) context.getAttribute("name");
//
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.write("我的名字:"+name);
}
}
web.xml 中注册Servlet。(先访问放入参数的Servlet)
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!-- 注册第二个Servlet -->
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.kuang.servlet.ServletTwo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
2、获取初始化参数
<!--配置一些web应用初始化参数( 注:与 <servlet>标签同级别 )-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
// servlet中读取初始化参数:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
3、请求转发
请求转发:url地址栏路径不变; 重定向:路径变成最新的

4、 重定向
@override
protected void doGet(HttpservletRequest req, HttpservletResponse resp) throws ServletException, IOException {
resp. sendRedirect("/r/img");//重定向
/*
上面代码,相当于下面两步:
resp. setHeader("Location","/r/img");
resp. setstatus (302);
*/
}
重定向与请求转发,页面都会跳转;请求转发(307)路径不会变化,重定向(302)路径会发生变化
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了ServletDemo04");
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径
//requestDispatcher.forward(req,resp); //调用forward实现请求转发;
context.getRequestDispatcher("/gp").forward(req,resp);
}
6.5 读取配置文件
src目录下,java、resources 都被打包到classes文件夹下,被称为 ClassPath
6.6 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器, HTTP请求中的信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息
获取参数
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式,解决乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password"); //获取字符串
String[] hobbys = req.getParameterValues("hobbys"); //获取数组
System.out.println("=============================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
System.out.println("=============================");
System.out.println(req.getContextPath());
//通过请求转发
//这里的 / 代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
6.7 HttpServletResponse
查看 ServletResponse 接口的方法:
// ---------- 负责向浏览器发送数据的方法 ----------
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
// ---------- 负责向浏览器发送响应头 ----------
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String varl,long var2)
void addDateHeader(String var1,long var2)
void setHeader(String var1,String var2);
void addHeader(String var1,String var2);
void setIntHeader(String var1,int var2);
void addIntHeader(String varl,int var2);
下载文件
- 获取文件的路径;设置下载名称
- 设置响应头,让浏览器支持下载
- 根据文件路径,获取输入流,将文件读取到内存
- 创建缓存区
- 获取OutputStream对象
- 将InputStream流写入缓存区;并通过OutputStream对象,将缓冲区中的数据输出到客户端
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取文件的路径
String path = "D:\\IdeaSpace\\javaweb_servlet01\\servlet_02\\src\\main\\resources\\一盏灯.jpg";
//设置下载名称
String downName = path.substring(path.lastIndexOf("\\")+1);
//2. 让浏览器能够支持(Content-Disposition)下载。中文文件名URLEncoder.encode编码,否则有可能乱码
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(downName,"UTF-8"));
//3. 根据文件路径,获取输入流,将文件读取到内存
InputStream inputStream = new FileInputStream(path);
//4.创建缓存区
int len = 0;
byte[] buffer = new byte[1024];
//5.获取OutputStream对象
ServletOutputStream outputStream = resp.getOutputStream();
//6.将InputStream流写入缓存区;
while ((len=inputStream.read(buffer))>0){
//通过OutputStream对象,将缓冲区中的数据输出到客户端
outputStream.write(buffer,0,len);
}
inputStream.close();
outputStream.close();
}
图形验证码
后端实现验证码功能:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器3秒自动刷新一次;
resp.setHeader("refresh","3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D g = (Graphics2D) image.getGraphics(); //笔
//设置图片的背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器
ImageIO.write(image,"jpg", resp.getOutputStream());
}
//生成随机数
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7-num.length() ; i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;
}
7. Cookie、Session
参考博客:REST设计原则
参考博客:session、cookie、token
7.1 会话
会话:指一个客户端与web服务器之间,连续发生的一系列请求和响应的过程。如:用户打开浏览器访问一个web网站,点击很多连接,关闭浏览器,这个过程可以称之为会话。
会话状态(session state):组件在先后多次交互之间维护的上下文(context)信息。
7.2 REST 设计原则
Representational State Transfer:表述性状态移交
REST是Web架构的设计原则。REST的无状态约束是指:交互必须在本质上是无状态的。客户端组件【不能利用】任何【服务端组件保存的会话状态】,从客户到服务器的每个请求,都必须包含理解该请求所必须的所有信息。因此,会话状态要全部保存在客户端。
无状态约束 存在的原因:
Web旨在成为一个互联网规模的分布式超媒体系统,使得信息服务的提供商必须满足无法控制的可伸缩性、组件的独立部署这两方面要求。
无法控制的可伸缩性是指:架构元素可能会与其组织边界之外的元素进行通信,当它们遇到以下的情况时仍能正常运行:意料之外的负载、收到错误或恶意的数据等等。
无状态约束【原因】:由于服务端组件随时可能遭遇意料之外的访问高峰/低谷,因此它必须具备迅速扩大 / 缩小服务规模的能力,即服务端组件的架构必须具备较好的可伸缩性。
- 当服务端不保存任何会话状态时:伸缩将比较容易,直接增加/减少物理服务器的台数即可。
- 当服务端保存了会话状态时:伸缩将比较困难,必须先解决会话共享问题(例如,把会话状态集中保存到中间服务器上,会增加架构的复杂性),才能考虑增加/减少物理服务器的台数。
【结论】:为了让Web架构可以同时具有简单性、可伸缩性,服务端组件不能保存任何会话状态。这需要客户端组件的配合:即交互时,客户端组件不能利用任何保存在服务端组件上的会话状态,即交互必须在本质上是无状态的(无状态约束)。
利大于弊
采用无状态约束给Web架构带来的好处:
- 改善了可见性:服务端不必为了确定一个请求的完整意图,而去查看其他请求。
- 改善了可靠性:各请求相互独立没有依赖,如果某个请求失败了,只需重发这一个请求即可。
- 改善了可伸缩性:服务端不必在多个请求之间保存状态,从而能更快释放资源并简化其实现。
- 允许每个交互独立于其他交互,使得交互的两个组件只需关注对方即可,无需了解Web整体的拓扑结构
采用无状态约束给Web架构造成的代价:
- 在一系列相关的请求中发送了重复数据,增加了每次交互的开销,可能会降低网络性能。
采用无状态约束能得到以下功能:
- 改善服务方连接器:连接器无需在请求之间保持状态,降低了连接器的物理资源消耗,并改善了连接器的可伸缩性。
- 允许并行交互:允许连接器并行地完成交互,连接器无需理解交互的语义。
- 允许负载均衡:使得中间组件只需根据本次请求提供的信息,就足以理解该请求的意图。
- 改善缓存连接器:由于无状态约束强制了每个请求都必须包含理解该请求所需的全部信息,包括是否能够重用已缓存响应的信息,因此,缓存连接器能够相对合理地发挥作用,即在以下两个互斥的问题上尽量取得平衡:减少网络交互次数带来的用户感知性能提升,减少缓存错配。
违背 无状态约束 的典型例子:基于session的会话管理机制
服务器先将登录用户的信息,保存在session对象中,作为后续请求的上下文(即会话状态)。当收到后续请求时,先获取该上下文才能理解后续请求的完整意图。即客户端组件向服务器提供的信息是不全的,服务器保存了会话状态。
【结论】基于session的会话管理机制违背了REST的无状态约束,其代价是造成来源服务器无法同时获得简单性、可伸缩性这两个架构属性,为了可伸缩,必须增加来源服务器架构的复杂性。
TCP、UDP、HTTP
| TCP | UDP | HTTP |
|---|---|---|
| 要求源端口、目的端口双方都要维护上下文,并确保双方记忆同步 | 对源端口、目的端口双方都不要求维护上下文 | 要求客户端维护上下文,服务端不维护 |
| 一个连接中的每次通信都是有依赖关系的,不独立 | 每一次通信都是独立的,无依赖关系 | 每个请求都是独立的,无依赖关系 |
HTTP 是无状态协议
HTTP的底层是有状态的TCP,但却是无状态协议:
- 所处的协议层不同:TCP是传输层协议,HTTP是应用层协议。
- 所要求的对象不同:TCP是对传输的两个端口提要求,要求双方维护状态。HTTP是对交互的两个组件提要求,要求客户端维护状态,服务端不维护状态。
- 所维护的状态不同:TCP维护的状态是指握手、数据包的序列号和确认号等与传输相关的信息。HTTP的状态通常是指用户登录凭证、购物车标识等与应用软件相关的信息。
Http是无状态协议,通过 cookie 和 session 记录请求的信息
CooKie
- cookie是在浏览器中记录一些数据,并且浏览器在访问相同服务器时会主动携带该数据
- 如果cookie丢失,或者换了一个浏览器再访问,那么cookie就不存在了。
Session
- session 是在服务器中记录相关状态数据,每个数据都有唯一的sessionID
- 把这个sessionID作为cookie存储在用户的浏览器中,session是依赖cookie的。
- 相对来说,可记录的数据量比较大,数据相对安全。
7.3 Cookie
java 中 cookie类一共是个属性:
| 属性名 | 作用 |
|---|---|
| name | Cookie名 |
| value | Cookie值 |
| domain | Cookie的域。如果设成.test.com,那么子域名a.test.com和b.test.com,都可以使用.test.com的Cookie。 |
| path | Cookie的路径。如果设成/path/,则只有路径为/path/的页面可以访问该Cookie。如果设为/,则本域名下的所有页面都可以访问该Cookie。 |
| expires / maxAge | Cookie的超时时间。若设置其值为一个时间,那么当到达此时间后,此Cookie失效。不设置的话默认值是Session,意思是Cookie会和Session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器)后,此Cookie失效。 |
| isHttpOnly | 若此属性为true,则只有在http请求头中会带有此Cookie的信息,而不能通过document.cookie来访问此Cookie |
| secure | 设置是否只能通过https来传递此条Cookie |
| SameSite | 用来防止 CSRF 攻击和用户追踪。可以设置三个值:Strict、Lax 和 None。Strict:最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 CookieLax:规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。None: 关闭SameSite属性,提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效 |
| priority | 定义了三种优先级,Low/Medium/High,当Cookie数量超出时,低优先级的Cookie会被优先清除 |
常用的方法:
// ---------- 获得Cookie ----------
Cookie[] cookies = req.getCookies();
// ---------- 操作cookie ----------
cookie.getName(); //获得cookie中的key
cookie.getValue(); //获得cookie中的vlaue
//新建一个cookie
new Cookie("lastLoginTime", System.currentTimeMillis()+"");
cookie.setMaxAge(24*60*60); //设置cookie的有效期,设置为0 立刻失效
// ---------- 返回cookie ----------
resp.addCookie(cookie);
- 一个Cookie只能保存一个信息;
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
- Cookie大小有限制4kb;300个cookie浏览器上限
// 编码与解码
URLEncoder.encode("秦疆","utf-8")
URLDecoder.decode(cookie.getValue(),"UTF-8")
7.4 Session
什么是Session
- session称为会话控制,存储用户会话所需的属性和配置信息。用户在应用程序的web页面跳转时,存储在session对象中的用户信息不会丢失,而是在整个用户会话中一直保持下去
- 服务器会给每一个用户(浏览器)创建一个session对象
- 一个session独占一个浏览器,只要浏览器不关闭,这个session就一直存在
Session中常用方法
| 方法 | 作用 |
|---|---|
| getId() | 获取session的唯一标识 |
| getServletContext() | 代表整个web服务 |
| getAttribute(String) | 获取session的节点 |
| setAttribute(String,Object) | 设置session节点 |
| removeAttribute(String) | 移除一个session节点 |
| isNew() | 判断一个session是否是一个新的session |
| invalidate() | 注销session的 |
Session使用场景
- 一个用户登陆后,访问该网站的其他网页时,一直处于登录状态
- 保存购物车信息等等
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到用户的session
HttpSession session = req.getSession();
//向session中保存数据
session.setAttribute("name","西南方向");
//获取session中的数据
Object name = session.getAttribute("name");
//获取session中的id
String sessionId = session.getId();
//判断session是否第一次生成
if(session.isNew()){
System.out.println("这个用户第一次访问该网站");
}
}

在web.xml中,可以配置session失效时间
<!--设置Session默认的失效时间-->
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>

8. JSP
什么是JSP
- Java Server Pages是一种动态网页技术标准。JSP部署于网络服务器上,响应客户端的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页。JSP技术以Java作为脚本语言,为用户的HTTP请求提供服务。
- JSP编译器把 JSP文件 编译成用
Java代码写的Servlet,再由Java编译器来编译成能 二进制机器码,也可以直接编译成二进制码。 - 写JSP就像在写HTML,但可以嵌入JAVA代码,为用户提供动态数据
8.1 JSP 原理
jsp本质上来说,就是一个Servlet。从继承关系来说,jsp文件编译成的java文件,是Servlet的实现类的子类。继承关系如下图:
index.jsp文件,会被编译成index_jsp.java,然后被编译成index_jsp.class文件,如下图:
jsp编译成的java文件,内置了很多对象,可以在页面上直接使用;同时会将HTML代码的部分,使用JspWriter对象输出到页面:
index_jsp.java文件,内置的方法、对象、将信息输出到页面的代码:
// -------------- jsp内置方法 -----------------
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(.HttpServletRequest request,HttpServletResponse response)
// -------------- jsp内置对象 -----------------
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //session
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //config
javax.servlet.jsp.JspWriter out = null; //out
final java.lang.Object page = this; //page:当前
HttpServletRequest request //请求
HttpServletResponse response //响应
// -------------- jsp中,将信息输出到页面的代码 -----------------
response.setContentType("text/html"); //设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
从请求到返回,jsp执行流程如下:
8.2 语法 & 指令
在项目中添加上下面的依赖:
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- jstl依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
jsp 的语法格式:
<%%> jsp片段
<%=%> jsp表达式
<%!%> jsp声明
<%--注释内容--%>
jsp表达式
<%--JSP表达式
作用: 用来将程序的输出,输出到客户端
格式: <%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>
<%-- EL表达式: ${}-->
${name}
jsp 脚本片段
<%--在代码嵌入HTML元素--%>
<% for (int i = 0; i < 5; i++) { %>
<h1>Hello,World <%=i%> </h1>
<% } %>
jsp声明
<%!
static {
System.out.println("Loading Servlet!");
}
private static int globalVar = 0;
public void kuang(){
System.out.println("进入了方法Kuang!");
}
%>
注:JSP声明 会被编译到JSP生成Java的类中!其他的,就会被生成到_jspService方法中!
jsp指令
<%@page args.... %>
<%@include file=""%>
<%--@include 会将多个页面,合成一个--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
<!-- ------------------------------ -->
<%--jSP标签
jsp:include:拼接页面,本质还是三个
--%>
<jsp:include page="/common/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"/>
8.3 九大内置对象
概述
JSP内置对象:JSP自带的,不需要new也能使用的对象
特点
- 内置对象是自动载入的,不需要直接实例化
- 内置对象通过web容器来实现和管理的
| 对象 | 作用 |
|---|---|
| pageContext | 网页的属性是在这里管理 |
| request | 用户端请求,此请求会包含来自GET/POST请求的参数 |
| session | 客户端与服务器的会话 |
| application | 全局对象,可将信息保存在服务器中,直到服务器关闭 |
| response | 服务器给客户端的响应 |
| out | 负责向客户端输出内容 |
| exception | 异常对象(需要页面设置isErrorPage属性参数) |
| page | page 对象代表JSP本身,只有在JSP页面内才是合法的 |
| config | config 对象的主要作用是取得服务器的配置信息 |
pageContext.setAttribute("name1","秦疆1号"); //只在一个页面中有效
request.setAttribute("name2","秦疆2号"); //只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","秦疆3号"); //只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","秦疆4号"); //只在服务器中有效,从打开服务器到关闭服务器
8.4 JSP标签、JSTL标签、EL表达式
<!-- JSTL表达式的依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
EL表达式: ${ }
作用: 获取数据、执行运算、获取web开发的常用对象
JSP标签
<%--
http://localhost:8080/jsptag.jsp?name=kuangshen&age=12
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="kuangshen"></jsp:param>
<jsp:param name="age" value="12"></jsp:param>
</jsp:forward>
JSTL表达式
STL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,功能和Java代码一样!
JSTL表达式:菜鸟教程

JSTL 标签库使用步骤
- 在jsp页面引入对应的 taglib,如核心库 <%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>
- 在Tomcat 的lib目录下,引入
jstl-api、standard包,否则会报错:JSTL解析错误
c:if
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin"> <!-- 将结果赋值给 isAdmin -->
<c:out value="管理员欢迎您!"/>
</c:if>
<%--自闭合标签--%>
<c:out value="${isAdmin}"/>
c:choose c:when
<%--定义一个变量score,值为85--%>
<c:set var="score" value="55"/>
<c:choose>
<c:when test="${score>=90}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为一般
</c:when>
<c:when test="${score>=70}">
你的成绩为良好
</c:when>
<c:when test="${score<=60}">
你的成绩为不及格
</c:when>
</c:choose>
c:forEach
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田六");
request.setAttribute("list",people);
%>
<%--
var , 每一次遍历出来的变量
items, 要遍历的对象
begin, 哪里开始
end, 到哪里
step, 步长
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/> <br>
</c:forEach>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
<c:out value="${people}"/> <br>
</c:forEach>
9. MVC三层架构
MVC概念
- Model, 处理业务
- View, 界面展示
- Controller,控制层,处理请求,调用视图与模型

控制器(serlvlet)用来接收浏览器发送过来的请求,控制器调用模型(JavaBean)来获取数据,比如从数据库查询数据;控制器获取到数据后再交由视图(JSP)进行数据展示。
项目分层
三层架构是将项目分成了三个层面,分别是 表现层、业务逻辑层、数据访问层
- 数据访问层:对数据库的CRUD基本操作
- 业务逻辑层:对业务逻辑进行封装,组合数据访问层层中基本功能,形成复杂的业务逻辑功能。
- 表现层:接收请求,封装数据,调用业务逻辑层,响应数据
整体流程
浏览器发送请求,表现层的Servlet接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法调用数据访问层方法进行数据的操作,依次返回到serlvet,然后servlet将数据交由 JSP 进行展示。
MVC 与三层架构的区别和联系

上半部分是 MVC 模式,下半部分是三层架构。 MVC 模式 中的 C(控制器)和 V(视图)就是 三层架构 中的表现层,而 MVC 模式 中的 M(模型)就是 三层架构 中的 业务逻辑层 和 数据访问层。
可以将 MVC 模式 理解成是一个大的概念,而 三层架构 是对 MVC 模式 实现架构的思想。
10. Filter(重点)
过滤器,用来过滤网站的请求和数据,如下图所示:
开发步骤:
- 编写过滤器,继承
javax.servlet.Filter接口,实现接口的方法
import javax.servlet.*;
import java.io.IOException;
public class CharacterFilter implements Filter {
//初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现!
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterFilter 初始化操作");
}
/*
filterChain : 过滤器链
doFilter中的代码,在执行过滤器的时候,都会执行
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//处理中文乱码
servletRequest.setCharacterEncoding("GBK");
servletResponse.setCharacterEncoding("GBK");
System.out.println("CharacterFilter 执行前");
//这行代码是固定写法,让过滤器继续往下走。如果不写,程序到此为止
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("CharacterFilter 执行后");
}
//web服务器关闭,开始销毁过滤器
@Override
public void destroy() {
System.out.println("CharacterFilter 销毁操作");
}
}
- 在web.xml中配置过滤器
<filter>
<filter-name>characterFilter</filter-name>
<filter-class>com.kuang.servlet.CharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>characterFilter</filter-name>
<!-- 只要是 /hello 开头的任何请求,都会走这个过滤器 -->
<url-pattern>/hello/*</url-pattern>
<!-- 过滤所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
11. Listener
监听器有很多接口,下面以 HttpSessionListener 为例,编写一个统计在线人数的监听器:
- 编写监听器类,实现
javax.servlet.http.HttpSessionListener接口:
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
//统计网站在线人数(session个数)
public class OnlineCountListener implements HttpSessionListener {
//创建一个session,在线人数 +1
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineNum = (Integer) servletContext.getAttribute("onlineNum");
if(onlineNum == null){
onlineNum = 1;
}else{
onlineNum = onlineNum ++;
}
servletContext.setAttribute("onlineNum",onlineNum);
}
//销毁一个session,在线人数 -1
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineNum = (Integer) servletContext.getAttribute("onlineNum");
if(onlineNum == null && onlineNum == 1){
onlineNum = 1;
}else{
onlineNum = onlineNum ++;
}
servletContext.setAttribute("onlineNum",onlineNum);
}
}
- 在web.xml中注册监听器
<listener>
<listener-class>com.kuang.OnlineCountListener</listener-class>
</listener>
12. Filter & Interceptor
参考博客:过滤器与拦截器区别
过滤器与拦截器的区别
| 过滤器 | 拦截器 |
|---|---|
基于回调函数实现,Filter的执行由Servlet容器回调完成 | 基于反射机制(动态代理)实现 ,通过动态代理的方式来执行 |
过滤器是Servlet的规范,需要实现javax.servlet.Filter接口,依赖于Tomcat等容器 | 拦截器是Spring组件,定义在org.springframework.web.servlet包下,由Spring容器管理,不依赖Tomcat等容器 |
| 过滤器无法获取IOC容器中的bean | 是spring提供并管理的,可以获取IOC容器中的各个bean。在拦截器里注入一个service,可以调用业务逻辑 |
| Filter的生命周期由Servlet容器管理 | 拦截器则可以通过IoC容器来管理 |
| 对请求在进入后Servlet之前或之后进行处理 | 对请求在handler【Controller】前后进行处理 |
| 过滤器可以修改request | 拦截器不能修改request |
| 用于预处理请求和响应,如过滤敏感词汇、sql防注入、设置字符编码等 | 用于验证信息,如用户是否登录、权限验证、日志记录等 |




