HTTP协议概述
主要可以理解为:客户端-发起请求=====服务器-响应请求
-
HTTP(Hyper Text Transfer Protocol):超文本传输协议
-
HTTP 协议是基于 TCP/IP 协议的
-
超文本:比普通文本更加强大
-
传输协议:客户端和服务器端的通信规则(握手规则)
请求部分
请求行 请求头 请求空行
请求的方式:get/post
注意:只有 POST 请求方式才有请求体
常用状态码介绍:
状态码 | 说明 |
---|---|
200 | 一切都OK> |
302/307 | 请求重定向(客户端行为,两次请求,地址栏发生改变) |
304 | 请求资源未发生变化,使用缓存 |
404 | 请求资源未找到 |
500 | 服务器错误 |
Servlet(重中之重)
servlet介绍:
Servlet 是运行在 Java 服务器端的程序,用于接收和响应来自客户端基于 HTTP 协议的请求
-
第一:Servlet是一个运行在web服务端的java小程序
-
第二:它可以用于接收和响应客户端的请求
-
第三:要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet
-
第四:每次请求都会执行service方法
-
第五:Servlet还支持配置
servlet的执行流程
Servlet关系视图
Servlet实现方式
共有3种方式(一般只用第三种)
-
第一种 实现 Servlet 接口,实现所有的抽象方法。该方式支持最大程度的自定义。
-
第二种 继承 GenericServlet 抽象类,必须重写 service 方法,其他方法可选择重写。该方式让我们开发 Servlet 变得简单。但是这种方式和 HTTP 协议无关。
-
第三种 继承 HttpServlet 抽象类,需要重写 doGet 和 doPost 方法。该方式表示请求和响应都需要和 HTTP 协议相关。
Servlet的生命周期
总结来说,只要有请求就是Servlet的诞生之日,请求结束服务器停止,Servlet死亡
Servlet的线程安全
和线程安全一样,需要加锁,注意上锁的对象要唯一
package com.itheima.servlet;
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;
/*
Servlet线程安全
*/
public class ServletDemo04 extends HttpServlet{
//1.定义用户名成员变量
//private String username = null;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String username = null;
synchronized (this) { //锁需要唯一,Servlet对象就是唯一的,所以用this
//2.获取用户名
username = req.getParameter("username");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//3.获取输出流对象
PrintWriter pw = resp.getWriter();
//4.响应给客户端浏览器
pw.print("welcome:" + username);
//5.关流
pw.close();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Servlet的映射方式
三种:(经常使用第一种)
-
第一种 具体名称的方式。访问的资源路径必须和映射配置完全相同
-
第二种 / 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么
-
第三种 通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径
注意:优先级问题。越是具体的优先级越高,越是模糊通用的优先级越低。第一种 -> 第二种 -> 第三种
Servlet多映射的使用场景
场景分析:
-
如果访问的资源路径是 /vip 商品价格打9折
-
如果访问的资源路径是 /vvip 商品价格打5折
-
如果访问的资源路径是其他 商品价格不打折
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
Servlet 多路径映射
*/
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 定义一个商品金额
int money = 1000;
//2. 获取访问的资源路径
String name = req.getRequestURI();
name = name.substring(name.lastIndexOf("/"));
//3. 条件判断
if("/vip".equals(name)) {
//如果访问资源路径是/vip 商品价格为9折
System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.9));
} else if("/vvip".equals(name)) {
//如果访问资源路径是/vvip 商品价格为5折
System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.5));
} else {
//如果访问资源路径是其他 商品价格原样显示
System.out.println("商品价格为:" + money);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
ServletConfig的使用
-
在
<servlet>
标签中,通过<init-param>
标签来配置。有两个子标签。 -
<param-name>
:代表初始化参数的 key。 -
<param-value>
:代表初始化参数的 value。
常用方法
ServletContext
作用:可以获得应用的全局初始化参数和达到 Servlet 之间的数据共享。
ServletContext图示:
域对象概念
-
域对象指的是对象有作用域。也就是有作用范围
-
域对象可以实现数据的共享
-
不同作用范围的域对象,共享数据的能力也不一样
-
在 Servlet 规范中,一共有 4 个域对象
ServletContext 就是其中的一个
ServletContext的使用
ServletContext 并不属于某个 Servlet 的配置,而是针对于整个应用的配置,也叫全局的初始化参数
在<web-app>
标签中,通过<context-param>
标签来配置。有两个子标签
<param-name>
:代表全局初始化参数的 key
<param-value>
:代表全局初始化参数的 value
配置Servlet,并且配置ServletContext
<web-app>
....
<!--配置Servlet-->
<servlet>
<servlet-name>servletContextDemo</servlet-name>
<servlet-class>com.itheima.servlet.ServletContextDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletContextDemo</servlet-name>
<url-pattern>/servletContextDemo</url-pattern>
</servlet-mapping>
<!--配置ServletContext-->
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>globalDesc</param-name>
<param-value>This is ServletContext</param-value>
</context-param>
</web-app>
常用方法
//获取ServletContext对象
ServletContext context = getServletContext();
//修改ServletContextDemo:存储数据
//向域对象中存储数据
context.setAttribute("username","zhangsan");
//修改ServletConfigDemo:获取数据
//获取ServletContextDemo设置共享的数据
Object username = context.getAttribute("username");
System.out.println(username);
自动注解开发Servlet
不需要web下的WEB-INF, web.xml也没有了
index.jsp也没用
Servlet应用案例-学生管理系统 ***
案例效果介绍
-
效果
-
访问案例首页,看到一个可以保存学生信息的界面
-
输入内容,点击保存,通过java服务器,然后最终保存到txt中
-
最后java服务器返回成功结果
-
案例实现
-
创建一个 web 项目:servlet_test,配置虚拟目录/stu
-
选择javaee 7,这里我们还是用web.xml
-
-
创建一个用于保存学生信息的 html 文件:web下新建addStudent.html
-
创建一个类com.itheima.servlet.StudentServlet,继承 HttpServlet
-
重写 doGet 和 doPost 方法
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class StudentServlet extends HttpServlet {
-
在 web.xml 文件中修改默认主页和配置 Servlet
<!--修改默认主页-->
<welcome-file-list>
<welcome-file>/addStudent.html</welcome-file>
</welcome-file-list>
<!--配置Servlet-->
<servlet>
<servlet-name>studentServlet</servlet-name>
<servlet-class>com.itheima.servlet.StudentServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>studentServlet</servlet-name>
<url-pattern>/studentServlet</url-pattern>
</servlet-mapping> -
在 doGet 方法中接收表单数据保存到文件中,并响应给浏览器结果
// -- ?username=张三&age=18&score=718
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取表单中的数据
String username = req.getParameter("username"); // 获取url后边的?的参数
String age = req.getParameter("age");
String score = req.getParameter("score");
//将数据保存到stu.txt文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\stu.txt",true));
bw.write(username + "," + age + "," + score);
bw.newLine();
bw.close();
//给浏览器回应
PrintWriter pw = resp.getWriter();
pw.println("Save Success~");
pw.close();
} -
部署并启动项目
-
通过浏览器测试
请求对象
请求对象常用方法1-获取各种路径
request.
获取请求参数工具类封装,可以使用apache的beanutils包
用流的形式读取请求信息
注意:
获取文字 字符时用getReader(字符流,如果采用字符流必须使用post请求方式)
获取图片 文件时用getInputStream(字节流)
请求正文中文编码问题
post方式请求
//设置编码格式
req.setCharacterEncoding("UTF-8");
get请求方式
//输出到浏览器:注意响应的乱码问题已经解决了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);
请求域
可以在一次请求范围内共享数据,一般用于请求,转发的多个资源中 共享数据
代码展示
响应对象
-
响应:回馈结果。在 BS 架构中,就是服务器给客户端浏览器反馈结果
-
响应对象:就是在项目中用于发送响应的对象
//解决中文乱码
resp.setContentType("text/html;charset=UTF-8");
案例:响应图片
-
创建字节输入流对象,关联读取的图片路径
-
通过响应对象获取字节输出流对象
-
循环读取和写出图片
-
案例:新建ServletDemo03
/*
响应图片到浏览器
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 创建字节输入流对象,关联图片路径
String realPath = getServletContext().getRealPath("/img/hm.png");
System.out.println(realPath);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//注意:不能直接指定图片路径,需要同getRealPath获取,因为项目还要发布,我们需要获取到发布之后的图片路径
//BufferedInputStream bis = new BufferedInputStream(new FileInputStream(/img/hm.png));
//2. 获取字节输出流对象
ServletOutputStream sos = resp.getOutputStream();
//3. 循环读写
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}
bis.close();
sos.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
设置缓存时间
String news = "这是一条很火爆的新闻~~";
//设置缓存时间:1小时的缓存时间
//参数1:Expires : 失效的意思
//参数2:当前时间+1小时毫秒值(意思就是在1小时之后过期)
resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L));
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(news);
System.out.println("aaa");
}
设置定时刷新
String news = "您的用户名或密码错误,3秒后自动跳转到登录页面...";
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(news);
//设置响应消息头定时刷新
//Refresh:刷新
//第二个参数第一部分:3,3设之后
//第二个参数第二部分:跳转到哪个路径
//resp.setHeader("Refresh","3;URL=/response/login.html");
resp.setHeader("Refresh","3;URL="+req.getContextPath()+"/login.html");
}
请求重定向
/*
请求重定向
*/
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求域数据
req.setAttribute("username","zhangsan");
//设置重定向
resp.sendRedirect(req.getContextPath() + "/servletDemo07");
//resp.sendRedirect("/response/servletDemo07");
// resp.sendRedirect("https://www.baidu.com");
}
/*
请求重定向
*/
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo07执行了...");
Object username = req.getAttribute("username");//获取不到
System.out.println(username);
}
总之一句话:重定向是两次请求,设置的数据不会共享
-
重定向与转发的区别
-
重定向:
-
两次请求
-
地址栏发生变化
-
不可以使用request域共享数据 (既然是两次请求,那肯定不能使用请求域中共享的数据)
-
可以重定向到其他服务器的url
-
-
转发:
-
一次请求
-
地址栏不发生变化
-
可以使用request域共享数据
-
只能转发到自己服务器内部的url
-
响应对象-文件下载
//1.创建字节输入流,关联读取的文件
//获取文件的绝对路径
String realPath = getServletContext().getRealPath("/img/hm.png");
//创建字节输出流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//2.设置响应头支持的类型 应用支持的类型为字节流
/*
Content-Type 消息头名称 支持的类型
application/octet-stream 消息头参数 应用类型为字节流
*/
resp.setHeader("Content-Type","application/octet-stream");
//3.设置响应头以下载方式打开 以附件形式处理内容
/*
Content-Disposition 消息头名称 处理的形式
attachment;filename= 消息头参数 附件形式进行处理;指定下载文件名称
*/
resp.setHeader("Content-Disposition","attachment;filename=hm.png");
//4.获取字节输出流对象
ServletOutputStream sos = resp.getOutputStream();
//5.循环读写文件
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}
//6.释放资源
bis.close();
//sos.close();
}