概念 (server applet ) : 执行在服务器端的小程序
- 就是一个接口,定义类Java类被浏览器访问到(tomcat识别)的规则。
- 我们要自定义一个类,实现Servlet接口,复写方法
快速入门
- 1、创建javaEE项目
- 2、定义一个类,实现Servlet接口
- public class Demo01Servlet implements Servlet
- 3、实现接口中的抽象方法
- 4、配置Servlet(在web.xml中配置s)
<!-- 配置servlet -->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.jiatengda.web.servlet.Demo01Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
执行原理
- 1、当服务器接收到客户端浏览器的请求后,会解析url路径,获取访问的Servlet的资源路径
- 2、查找web.xml , 是否有对应的<url-pattern>标签体内容
- 3、如果有,则找对应的<servlet-class>类名
- 4、 tomcat会将字节码文件加载进内存,并且创建对象
- 5、调用其方法
Servlet 生命周期
- 被创建 : 执行init方法,只执行一次
- Servlet什么时候被创建?
- 默认情况下,第一次访问时,Servlet被创建 - 可以配置Servlet创建时期- init只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。
- 多个用户同时访问时,可能存在线程安全问题
- 解决 : 尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对其修改值。
<!-- 执行Servlet创建时机
1. 第一次被访问时创建
值为负数,则第一次被访问时被创建
2. 在服务器启动时创建
值为0或者正整数
-->
<servlet>
<load-on-startup>-5</load-on-startup>
</servlet>
- 提供服务 : 执行service方法,执行多次
- 每次访问时都被执行一次
- 被销毁 : 执行destroy方法 , 执行一次
- Servlet被销毁时执行,服务器关闭时,Servlet被销毁
- 只有服务器正常关闭时才会执行destroy方法
- 一般用于释放资源
package cn.jiatengda.web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class Demo02Servlet implements Servlet {
/**
* 初始化方法
* 在Servlet创建时被执行,只会执行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init .. ");
}
/**
* 获取servletConfig对象 : servlet配置对象
* @return
*/
@Override
public ServletConfig getServletConfig() {
System.out.println("config .. ");
return null;
}
/**
* 提供服务方法
* 每一次Servlet被访问时被执行,执行多次
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service .. ");
}
/**
* 获取Servlet的一些信息,版本、作者等等...
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁方法
* 在Servlet被杀死时执行,执行一次,服务器关闭时执行
*/
@Override
public void destroy() {
System.out.println("destroy .. ");
}
}
Servlet 3.0
- 好处 :
- 支持注解配置,不需要web.xml
- 步骤
- 1、创建JavaEE项目,选择Servlet版本3.0以上,可以不创建web.xml
- 2、定义一个类,实现Servlet接口
- 3、复写方法
- 4、在类上使用@WebServlet注解配置
- @WebServlet(“/资源路径”)
IDEA与Tomcat相关配置
- IDEA会为每一个tomcat部署的项目单独建立一份配置文件
- 工作空间项目 和 tomcat部署的web项目
- tomcat真正访问的是
tomcat部署的web项目,tomcat部署的web项目对应着工作空间项目web目录下的所有资源 - WEB-IINF目录下的资源不能直接被浏览器访问
- 断点调试 : 使用
小虫子debug 启动
- tomcat真正访问的是
Servlet 体系结构
Servlet 接口
- GenericeServlet : 抽象类 , 将Servlet接口中其他方法做了默认的空实现
- HttpServlet : 抽象类,对http协议的一中封装,简化操作
- 定义类继承HttpServlet
- 复写了doGet/doPost 方法
Servlet 相关配置
- urlpartten : Servlet 访问路径 : 一个Servlet可以定义多个访问路径 : @WebServlet({“/d5”,”/demo5”})
- 路径定义规则
- 1、/xxx
- 2、/xxx/xxx
- 3、*.do
HTTP :
概念 :Hyper Text Teansfer Protocol 超文本传输协议
传输协议 :定义了客户端和服务器端通信时,发送数据的格式
- 特点:
- 1、基于TCP/IP的高级协议
- 2、默认端口号是80
- 3、基于请求/响应模型的,一次请求对应一次响应
- 4、无状态的: 每次请求之间相互独立,不能交互数据
- 特点:
历史版本:
- 1.0 每一次请求响应都会建立新的连接
- 1.1 复用链接
- 请求消息数据格式
- 请求行
- 请求方式 请求url 请求的协议/版本
- GET login.html HTTP/1.1
- 请求方式 :
- HTTP协议有7中请求方式,常用的两种
- POST
- GET
- HTTP协议有7中请求方式,常用的两种
- 请求头 :客户端浏览器告诉服务器一些信息
- 请求头名称 : 请求头的值
- 常见的头
- 1、User-Agent : 浏览器告诉服务器,我访问你使用的浏览器版本信息
- 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- 2、Referer :
- 告诉服务器,我(当前请求)从哪里来?
- 作用
- 1、防盗链 :
- 2、统计工作 :
- 作用
- 告诉服务器,我(当前请求)从哪里来?
- 1、User-Agent : 浏览器告诉服务器,我访问你使用的浏览器版本信息
- 请求空行
- 空行,用于分割post请求的请求头和请求体的
- 请求体 (正文)
- 封装post请求消息的请求参数的
- 请求行
- 响应消息数据格式
Request
request对象和response对象的原理
- 两个对象是由服务器创建的,我们来使用他们
- request是来获取请求消息
- response是来设置响应消息
request 对象的继承体系结构
- ServerletRequest : 接口
- HttpServerletRequest : 接口,继承ServerletRequest接口
- RequestFacade extends HttpServerletRequest : 实现类 (tomcat)
request功能 :
- 获取请求消息 :
- 获取请求行数据
- String getMethod() : 获取请求方式(GET)
获取虚拟目录: String getContextPath- 获取Servlet路径 : String getServletPath()
- 获取get方式的请求参数: String getQueryString()
获取请求的URI: String getRequestURI()- 获取请求的URL : String getRequestURL()
- 获取协议版本: String getProtocol()
- 获取客户机的IP地址 : String getRemoteAddr()
- 获取请求行数据
package cn.jiatengda.web.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;
@WebServlet("/demo8")
public class Demo06Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
String contentPath = req.getContextPath();
String requestURI = req.getRequestURI();
StringBuffer requestURL = req.getRequestURL();
System.out.println("请求方式: " + method);
System.out.println("虚拟目录: " + contentPath);
System.out.println("请求URI: " + requestURI);
System.out.println("请求URL: " + requestURL);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
获取请求头数据
String getHeader(String name): 通过请求头的名称获取请求的值- Enumeation<String> getHeaderNames() : 获取所有请求头名称
获取请求体数据
- 1、获取对象
- 字节流 : BufferReader getReader() : 获取字符输入流,只能操作字符数据
- 字符流 : ServletInputStream getInputStream() : 获取字节输入流,可以操作所有数据类型
- 2、再从对象中拿数据
- 1、获取对象
其他功能:
- 1、获取请求参数通用方式 :不论get还是post请求方式都可以使用下列方法来获取请求参数
- String getParameter(String name) : 根据参数名称获取参数值
- Srring[] getParameterValues(String name) : 根据参数名称获取参数值的数组
- Enumeation<String> getParameterNames(): 获取所有请求的参数名称
- Map<String , String[]> getParameterMap() : 获取所有参数的Map集合
- 2、请求转发 : 一种在服务器内部的资源跳转方式
- 1、通过request对象获取请求转发对象 : RequestDispatcher getRequestDispatcher(String path)
- 2、使用 RequestDispatcher对象进行转发: forward(ServletRequest request , ServletResponse response)
- 特点:
- 1、浏览器地址栏路径不发生变化
- 2、只能转发到当前的服务器内部资源中
- 3、转发是一次请求
- 3、共享数据 :
- 域对象 : 一个有作用范围的对象,可以在范围内共享数据
- request域 : 代表一次请求的范围,一般同于请求转发的多个资源中共享数据
- 方法:
- 1、void setAttribute(String name , Object obj) : 存储数据
- 2、Object getAttriibute(String name) : 获取数据
- 3、void removeAttribute(String name) : 通过键移除键值对
- 4、获取ServletContext :
- ServletContext getServletContext()
- 1、获取请求参数通用方式 :不论get还是post请求方式都可以使用下列方法来获取请求参数
中文乱码问题:
- get方式 : tomcat8 已经将get方式解决了
- post方式: 会乱码
- 解决: 在获取参数前,来设置请求request的编码=>request.setCharsetEncoding(‘utf-8’);
BeanUtils工具类,简化数据封装
- 用于封装JavaBean的
- JavaBran : 标准的Java类
- 1、类必须是public修饰
- 2、必须提供空参的构造器
- 3、成员变量必须使用priivate修饰
- 4、提供公共的getter和setter方法
- 功能: 封装数据
- 概念 :
- 成员变量:
- 属性 : setter和getter方法截取后的产物
- 例如 getUsername –> Username –> username
- 方法:
- 1、setProperty()
- 2、getProperty()
- 3、populate(Object obj , Map map) : 将map集合的键值对信息,封装到对应的JavaBean对象中
HTTP协议 : 响应消息
响应消息 : 服务器端发送给服务端的数据、
- 响应状态码 : 服务器告诉客户端浏览器本次请求与和响应的一个状态
- 状态码都是三位数字
- 响应头 :
- Content-Type : 服务器告诉客户端响应体数据格式以及编码格式
- Content-disposition : 服务器告诉客户端以什么个是你打开响应体数据
- in-line : 默认值,在当前页面打开
- attachment;filename=xxx: 以附件形式打开响应体。文件下载
- 响应空行
- 响应体 :传输的数据
Reponse 对象
- 设置响应头 : setHeader(String name , String name)
- 设置响应体 :
- 获取输出流
- 字符输出流 : PrintWriter getWriter()
- 字节输出流 : ServletOutputStream getOutputStream()
- 使用输出流,将数据输出到客户端浏览器
- 获取输出流
response方法
- sendRedirect(String path) : 重定向
- 转发和重定向特点 :
- 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求
- 重定向地址栏发生变化
- 重定向可以访问其他服务器的资源
- 重定向是两次请求。不能使用request对象共享数据
- 路径path的写法 :
- 1、路径的分类
- 相对路径 : 通过相对路径不可以确定唯一资源
- 不以斜 / 开头,以点开头的,./index.html
- 规则 : 确定访问的当前资源和目标资源之间的相对位置关系
- ./ : 当前目录
- ../ : 上级目录
- 绝对路径 : 通过绝对路径可以确定唯一资源
- 以 / 开头的路径 /index.html
- 规则: 判断定义的路径给谁用
- 客户端浏览器 : 需要加虚拟路径(项目的访问路径 )
- 建议虚拟目录动态获取
- 重定向,a标签 、、、
- 服务器 : 不需要加虚拟路径
- 转发路径
- require.getContextPath() : 动态获取虚拟目录,防止以后修改目录名而出错
- 客户端浏览器 : 需要加虚拟路径(项目的访问路径 )
- 相对路径 : 通过相对路径不可以确定唯一资源
- 1、路径的分类
- 转发和重定向特点 :
服务器输出字符数据到浏览器
- 步骤 :
- 1、获取字符输出流
- 2、输出数据
- 注意 : 乱码问题
- 原因 : 编解码使用的字符集不一致
- 解决 : 在获取流对象前设置 resp.setContentType(“text/html;charset=utf-8”);
服务器输出字节数据到浏览器
- 步骤 :
- 1、获取字节输出流
- 2、输出数据
- 注意 : 乱码问题
- 原因 : 编解码使用的字符集不一致
- 解决 : 在获取流对象前设置 resp.setContentType(“text/html;charset=utf-8”);
package cn.jiatengda.servlet.requestDemo;
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("/demo1")
public class Demo01Request extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 中文乱码原因 : 编解码使用的字符集不一致 , 需要在获取流对象之前设置默认编码格式
// resp.setCharacterEncoding("utf-8");
// 告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码
// resp.setHeader("content-type","text/html;charset=utf-8");
// 简单的形式来设置编码
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
pw.write("<h1>你好啊啊啊啊啊 , response</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
案例 : 验证码
- 为了防止用户恶意攻击,比如无限注册等
package cn.jiatengda.servlet.requestDemo;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/demo3")
public class Demo03RequestCode extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int width = 100;
int height = 50;
// 1. 创建一个对象 , 在内存中图片(验证码的图片对象)
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
// 2. 美化图片
// 2.1 填充背景色
Graphics g = image.getGraphics(); // 画笔对象
g.setColor(Color.PINK); // 设置画笔颜色
g.fillRect(0,0,width,height);
// 2.2 画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width-1,height-1);
String str = "AQWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890";
// 生成随机角标
Random ran = new Random();
for (int i = 1 ; i <= 4; i++) {
int index = ran.nextInt(str.length());
// 获取字符
char ch = str.charAt(index);
// 2.3 写验证码
g.drawString(ch + "",width/5*i,height/2);
}
// 2.4 画干扰线
g.setColor(Color.GREEN);
for (int i = 0; i < 10; i++) {
// 随机生成坐标点
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);
int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
// 3. 将图片输出到页面展示
ImageIO.write(image,"jpg",resp.getOutputStream());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
ServletContext对象
概念 : 代表整个web应用,可以和程序的容器(服务器)来通信
获取
- 1、通过request对象获取 : request.getServletContext()
- 2、通过httpServlet获取 : this.getServletContext()
- 两种获取方式获取的对象相等
功能
- 1、获取MIME类型 :
- MIME类型: 在互联网通信过程中定义的一种文件数据类型
- 格式: 大类型/小类型 text/html image/jpeg
- 获取 :String getMimeType(String file)
- MIME类型: 在互联网通信过程中定义的一种文件数据类型
- 2、域对象 : 共享数据
- setAttribute(String name , Object value)
- getAttribute(String name)
- removeAttribute(String name)
- ServletContext对象范围 : 所有用户所有请求的数据
- 3、获取文件的真实(服务器)路径
- 方法: String getRealPath(String path)
@WebServlet("/context2")
public class Demo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过request对象获取
ServletContext context = req.getServletContext();
// 获取文件的服务器路径
String realPath = context.getRealPath("/a.txt");// web目录下资源访问
System.out.println(realPath);
String b = context.getRealPath("/WEB-INF/b.txt");// WEB-INF目录下的资源访问
System.out.println(b);
String c = context.getRealPath("/WEB-INF/classes/c.txt"); // 访问src目录下的资源
System.out.println(c);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
案例 : 文件下载
- 页面显示超链接
- 点击超链接弹出下载提示框
- 完成图片文件下载
步骤 :
- 1、定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
- 2、定义Servlet
- 获取文件名称
- 使用字节输入流加载文件进内存
- 指定response的响应头 : content-disposition : attachment;filename=xxx
- 将数据写出到response输出流
问题 :中文文件名
- 解决思路 :
- 获取客户端使用的浏览器版本信息
- 根据不同的版本信息,设置不同的编码格式
public class ServletDownload extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数,文件名称
String filename = req.getParameter("filename");
// 2.使用字节输入流加载文件进内存
// 2.1找到服务器真是路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/" + filename);
// 2.2 使用字节流关联
FileInputStream fis = new FileInputStream(realPath);
// 3. 设置response的响应头
//3.1 设置类型头
resp.setContentType(servletContext.getMimeType(filename));
//3.2 设置打开方式
resp.setHeader("content-disposition" , "attachment;filename=" + filename);
// 4. 将输入流的数据写出到输出流中
ServletOutputStream sos = resp.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}