Servlet
- 概念:运行在服务器端的小程序
- Servlet就是一个接口,定义了java类被浏览器访问到(tomcat识别)的规则
- 我们自定义一个类,实现Servelet接口,复写方法
快速上手
1.创建JavaEE项目
-
注意:创建过程中不要有中文路径
-
步骤
1.启动IDEA
2.新建一个工程
3.选择JavaEnterprise
4.勾选Web Application,勾选Create web.xml
5.然后下一步,输入项目名和路径(不要中文路径)
2.定义一个类,实现Servlet接口
package cn.zqq.web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("这是一个开始");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
3.实现接口中的抽象方法
由于要快速上手,所以只需要在service方法里写一个输出就行
System.out.print("快速上手Servlet。");
4.配置Servlet
注意:项目有中文路径的话这一步可能没法实现
(1)在web
–>WEB-INFO
中,找到web.xml
(2)在里面添加如下内容
servlet-name
标签中的值自己填servlet-class
标签中的值是全路径(包名+类名)servlet-mapping
中的servlet-name
和上面的相同servlet-mapping
中的url-pattern
可以自己随便写,前面必须加/
<?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">
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>cn.zqq.web.servlet.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
</web-app>
(3)运行tomcat服务器
直接输入网址http://localhost:8080/demo
访问(demo是刚才的url-pattern)
运行成功 会在IDEA控制台输出"快速上手Servlet。"
-
注意:可能输入网址后会显示404
-
解决方法
点击IDEA菜单栏里的
Run
菜单,找到Edit Configurations
查看Tomcat的
Server
菜单中URL
的配置是否为http://localhost:8080/
以及Tomcat的
Deployment
菜单中的Application context
是否为/
如果不是,需要修改成这个就可以通过
http://localhost:8080/demo
访问了(也可以根据自己的需要进行设置)
-
Servlet执行原理
-
在浏览器中输入一个url地址(http://localhost:8080/demo)时
-
通过
ip
和端口号
(localhost:8080)可以准确定位到主机上的应用程序 -
我们在Tomcat的Edit Configurations中将项目部署(Deployment)的路径设置成了
/
-
tomcat内部会查找配置文件web.xml,定位到资源文件/demo
-
然后tomcat就能通过/demo找到servlet-name为demo的cn.zqq.web.servlet.ServletDemo这个类
这边tomcat将全类名对应的字节码文件加载进内存,然后创建其对象,调用这个类里的方法,所以service方法就被成功的执行了
(即tomcat进行了反射操作) -
service方法被执行后,我们就看到了在控制台中输出的结果
Servlet生命周期
1.被创建
被创建时,会自动执行初始化方法init
,只会执行一次
-
什么时候被创建?
根据配置文件
web.xml
中的<load-on-startup>
的值来定(默认值为-1)
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>cn.zqq.web.servlet.ServletDemo</servlet-class>
<load-on-start-up></load-on-start-up>
</servlet>
-
当
<load-on-start-up>
的值为负数,就在第一次被访问时创建 -
当
<load-on-start-up>
的值为0或正整数,就在服务器启动时创建 -
Servlet的init方法只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
- 多个用户同时访问时,可能存在线程安全问题
- 解决: 尽量不要在Servlet中定义成员变量。即使定义了成员变量也不要修改它的值
2.提供服务
在每次被访问时,会自动执行提供服务的方法service
,可以执行多次
3.被销毁
在服务器正常关闭时,会自动执行销毁方法destory
,只会执行一次
但是,如果服务器没有正常关闭,就不会执行destory方法
destory是在Servlet被销毁之前执行
-
Servlet还有两个方法
- 获取ServletConfig对象的方法:
getServletConfig()
(不常用) - 获取Servlet的信息的方法(版本、作者等信息):
getServletInfo()
(不常用)
- 获取ServletConfig对象的方法:
Servlet注解配置
-
注意:注解配置需要在Servlet版本3.0以上才可以(要求 Java EE6版本及其以上)
-
好处
使用注解配置,就可以不用创建web.xml了,写起来会更加方便
-
步骤
1.创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
2.定义一个类,实现Servlet接口
3.复写方法
4.在类上使用@WebServlet注解,进行配置
@WebServlet("资源路径")
说明:相当于刚才tomcat通过url中的/demo找到web.xml定位到我们实现Servlet的那个类的这个过程,用一个注解来实现了(因为注解直接写在类上面了,所以就直接定位到这个类)
package cn.zqq.web.servlet;
import javax.jws.WebService;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo")
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("快速上手Servlet。");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
Servlet的体系结构
Servlet --接口
GenericServlet --抽象类 是Servlet的实现类
HttpServlet --抽象类 是GenericServlet的子类
- GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
- 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
- HttpServlet:对http协议的一种封装,简化操作
- 定义类继承HttpServlet
- 复写doGet/doPost方法
Servlet相关配置
1.urlpartten:Servlet访问路径
-
一个Servlet可以定义多个访问路径:@WebServlet({"/demo","/demo2","/demo3"})
-
路径定义规则:
-
/xxx
:一层路径 -
/xxx/xxx:
两层路径,以后访问的时候就需要写两层路径- 使用
*
通配符时,不管写什么都能访问到/*
- 使用
-
*.do
这种不需要加/,但是需要以.do
结尾,.do
也可以换成其他自己定义的后缀
-
HTTP
HTTP概述
概念:Hyper Text Transfer Protocol 超文本传输协议
-
传输协议:定义了客户端和服务器端通信时,发送数据的格式
-
特点
1.基于TCP/IP的高级协议
2.默认端口号:80
3.基于请求/响应模型:一次请求对应一次响应
4.无状态:每次请求之间相互独立,不能交互数据
-
历史版本
- 1.0:每一次请求响应都会建立新的连接
- 1.1:复用连接
请求消息:客户端发送给浏览器发送的数据
1.请求行
格式:请求方式 请求url 请求协议/版本
示例:GET /login.html HTTP/1.1
-
请求方式
-
HTTP协议有7种请求方式,常用的有2种
-
GET
1.请求参数在请求行中,在url后
http://localhost/demo?username=zhangsan
2.请求的url长度有限制的
3.相对不太安全(参数在url后面,容易被看到)
-
POST
1.请求参数在请求体中
2.请求的url长度没有限制
3.相对安全(如果请求被拦截也是可以被看到的)
-
-
2.请求头
客户端浏览器告诉服务器一些信息
格式: 请求头名称:请求头值
-
常见的请求头
-
User-Agent:浏览器告诉服务器,访问服务器所用的浏览器版本信息
可以在服务器端获取该头的信息,解决浏览器的兼容性问题
在服务器端,获取浏览器的版本,响应对应版本的页面给服务器
-
Referer:告诉服务器,当前请求从哪里来
-
作用
1.防盗链(判断请求从哪来,如果是自己网站来的,就可以访问,如果是别人的网站,就不让访问,不把资源响应过去,防止自己的资源被通过链接的方式盗用)
2.统计( )
-
-
3.请求空行
用于分割POST请求的请求头和请求体
4.请求体(正文)
拥有封装POST请求消息的请求参数
响应消息:服务器端发送给客户端的数据
1.响应行
- 组成:协议/版本 响应状态码 状态码描述
- 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态
- 状态码都是3位数字
- 分类:
- 1xx: 服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码
- 2xx: 成功 代表:
200
- 3xx: 重定向 代表:
302
(重定向)304
(访问缓存) - 4xx: 客户端错误
- 代表:
404
(请求路径没有对应的资源)405
(请求方式没有对应的doGet/doPost方法)
- 代表:
- 5xx: 服务器端错误。代表:500 (服务器内部出现异常)
2.响应头
-
格式: 头名称: 值
-
常见的响应头
Content-Type :服务器告诉客户端本次响应体数据格式以及编码格式
Content-disposition:服务器告诉客户端以什么格式打开响应体数据
值:
1.in-line:默认值,在当前页面内打开
2.attachment;filename=xxx:以附件形式打开响应体。文件下载
3.响应空行
4.响应体:真实传输的数据
Request
1.Request对象和Response对象原理
- 原理
1.tomcat服务器会根据url中的资源路径,创建对应的ServletDemo对象
2.tomcat服务器,会创建request和response对象,request对象中封装请求消息数据
3.tomcat将request和response两个对象传递给service方法,并调用service方法
4.开发者在service中通过request对象获取请求消息数据,通过response对象设置响应消息数据
-
注意
1.request对象和response对象是由服务器创建的,我们来使用它们
2.request对象是来获取请求消息,response对象是来设置响应消息
2.request对象继承体系结构
-
ServletRequest
–接口 -
HttpServletRequest
--接口 继承于ServletRequest -
org.apache.catalina.connector.RequestFacade
--类(tomcat)实现了
HttpServletRequest
接口
3.request功能
-
获取请求消息
-
获取请求行数据
方法:
-
获取请求方式:
getMethod()
返回值:String类型
-
获取虚拟目录:
getContextPath()
返回值:String类型
-
获取Servlet路径:
getServletPath()
返回值:String类型
-
获取get方式请求参数:
getQueryString()
返回值:String类型
-
获取请求URI:
1.
getRequestURI()
返回值:String类型
2.
getRequestURL()
返回值:StringBuffer类型
-
获取协议及版本:
getProtocol()
返回值:String类型
-
-
获取请求头数据
方法:
- 通过请求头的名称获取请求头的值:
getHeader(String name)
返回值:String类型
- 获取所有的请求头名称:
getHeaderNames()
返回值类型:Enumeration<String>
- 通过请求头的名称获取请求头的值:
-
获取请求体数据
-
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
-
步骤
1.获取流对象(有两个方法)
方法1:
getReader()
返回值:BufferedReader类型
:获取字符输入流,只能操作字符数据方法2:
getInputStream()
返回值:ServletInputStream类型
:获取字节输入流,可以操作所有数据2.再从流对象中拿数据
-
-
-
其他功能(常用)
- 获取请求参数通用方式(get post都可以用)
1.String getParameter(String name):根据参数名称获取参数值
2.String[] getParameterValues(String name):根据参数名称获取参数值的数组
3.Enumeration<String> getOarameterNames():获取所有请求的参数名称
4.Map<String,Stringp[]> getPrameterMap():获取所有参数的map集合
中文数据乱码问题解决
get方式:tomcat 8 及以上版本 已经将get方式乱码问题解决了
post方式:设置流的编码:`setCharacterEncoding("utf-8");`
-
请求转发
-
共享数据
-
获取ServletContext
-
request请求转发
一种在服务器内部的资源跳转方式
步骤
1.通过request对象获取请求转发器对象:
RequestDispatcher getRequestDispatcher(String Path)
2.使用RequestDispatcher对象来进行转发:
forward(ServletRequest request,ServletResponse response)
3.特点:
(1)浏览器地址栏路径没有发生变化
(2)只能转发到当前服务器内部的资源中
(3)转发是一次请求
-
request共享数据
域对象:一个有作用范围的对象,可以在范围内共享
request的域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
方法:
1.setAttritube(String name,Object obj):存储数据(在转发之前)
2.Object getAttritube(String name):通过键值获取
3.void removeAttribute(String name):通过键值对移除
-
request获取ServletContext
方法:
ServletContex getServletContext()
Response
1.response 功能:设置响应消息
1.设置响应行
(1)格式:HTTP/1.1 200 OK
(2)设置状态码:setStatus(int sc)
2.设置响应头setHeader(String name,String value)
3.设置响应体
使用步骤:
(1)获取输出流
-字符输出流: printWriter getWriter()
-字节输出流: ServletOutputStream getOutputStream
(2)使用输出流,将数据输出到输出流
2.重定向
当浏览器访问A资源时,A资源告诉浏览器,需要访问B资源
1.告诉浏览器重定向: 状态码302
2.告诉浏览器B资源的路径: 响应头location:B资源的路径
response.setStatus(302);
response.setHeader("location","这里写B资源路径");
也可以用简单的重定向方法: response.sendRedirect("这里写B资源路径")
-
重定向的特点
-
地址栏发生变化
-
重定向可以访问其他站点的资源
-
重定向是两次请求,不能使用request对象来共享数据
-
-
路径写法
-
相对路径
-
如:
./index.html
-
不以
/
开头,以.
开头的路径 (./可以省略不写) -
规则:找到当前资源和目标资源之间的相对位置关系
-
当前目录:
./
-
后退一级目录
../
-
-
-
绝对路径:通过绝对路径可以确定唯一资源
-
如:
http://localhost/img/test.jpg
/img/test.jpg -
以
/
开头的路径 -
规则:判断定义的路径是给谁用的, 判断请求将来从哪发出
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:
request.getContextPath()
- 建议虚拟目录动态获取:
- 给服务器使用:不需要加虚拟目击了
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
-
说明
- 转发,就不需要写虚拟目录,直接写资源目录
- 重定向是客户端路径,需要写虚拟目录
-
-
3.服务器输出字符数据到浏览器
- 步骤:(需要解决乱码)
//获取流对象之前,获取流的默认编码: ISO-8859-1 比如 设置为:utf-8
// response.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码,建议使用该编码解码
// response.setHeader("content-type","text/html;charset=utf-8");
//简单的方式设置编码
response.setContentType("text/html;charset=utf-8");
//获取字符输出流
PrintWriter pw = response.getWriter();
//输出数据
pw.write("<h1>测试输出</h1>");
4.服务器输出字节数据到浏览器
- 步骤
//设置编码
response.setContentType("text/html;charset=utf-8");
//获取字符输出流
ServletOutputStream ss= response.getOutputStream();
//输出数据(字节流输出字符串要用getBytes方法)
ss.write("hello response".getBytes("utf-8"));
5.验证码
-
本质:图片
-
目的:防止恶意表单注册
-
步骤
int width = 100;
int height = 50;
//1.创建一个对象,在内存中图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.Type_INT_RGB)
//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)
//2.3随机生成验证码
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
//生成随机角标
Random ran = new Random();
for(int i = 1;i<=4;i++){
int index = ran.nextInt(str.length());
char ch = str.charAt(index);//随机字符
g.drawString(ch+"",width/5*i,height/2);
}
//2.4画干扰线
g.setColor(Color.GREEN);
//随机生成坐标点
for(int i = 1;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",response.getOutputStream());
- 点击切换图片,只需要在图片的点击事件中修改url地址
var img = document.getElementById("checkCode");
img.onclick = function(){
//加时间戳是为了图片能不断刷新,避开浏览器的内存
var date = new Data().getTime();
img.src = "/test/checkCodeServlet?"+date;
}
ServletContext对象
-
概念:代表整个web应用,可以和程序的容器(服务器)来通信
-
获取:
-
通过request对象获取
request.getServletContext();
-
通过HttpServlet获取
this.getServletContext();
-
-
功能
-
获取MIME类型
- MIME类型:在互联网通信过程中定义的一种文件数据类型
- 格式: 大类型/小类型 text/html image/jpeg
- 获取:String getMimeType(String file);
-
域对象:共享数据
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
- ServletContext对象范围:所有用户所有请求的数据
-
获取文件的真实(服务器)路径
方法:String
getRealPath()
-
//通过HttpServlet获取
ServletContext context = this.getServletContext();
//获取文件的服务器路径
String realPath = context.getRealPath("/b.txt");//web目录下访问资源
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
文件下载
文件下载需求
- 页面显示超链接
- 点击超链接后弹出下载提示框
- 完成图片文件下载
分析
-
超链接指向的资源如果能够被浏览器解析,则在浏览器中展示(比如图片),如果不能解析,则弹出下载提示,不满足需求
-
我们需要任何资源都必须弹出下载提示
-
解决: 使用响应头设置资源的打开方式
content-disposition:attachment;filename = xxx
步骤
-
定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
-
定义Servlet
- 获取文件名称
- 使用字节输入流加载文件进内存
- 指定response的响应头:content-disposition:attachment;filename = xxx
- 将数据写出到response输出流
//1.获取文件名称
String filename = request.getParameter("filename");
//2.使用字节输入流加载文件进内存
//2.1找到文件服务器路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/"+filename);
//2.2使用字节流关联
FileInputStream fis = new FileInputStream(realPath);
//2.3将输入流的数据写出到输出流中
//3.指定response的响应头:content-disposition:attachment;filename = xxx
//3.1设置响应头 content-type
String mimeType = servletContext.getMimeType(filename);
response.setHeader("content-type",mimeType);
//3.2设置响应头打开方式:content-disposition
response.setHeader("content-disposition","attachment;filename="+filename);
//4.将数据写出到response输出流
ServletOutoutStream ss = response.getOutPutStream();
byte[] buff = new byte[1024*8];
int len = 0;
while((len = fis.read(buff))!=-1){
ss.write(buff,0,len);
}
3.中文文件乱码问题
-
解决思路
- 获取客户端使用的浏览器版本信息
- 根据不同的浏览器版本信息,设置不同的编码信息,响应不同的数据
String agent = request.getHeader("user-agent");
//工具类DownLoadUtils 可以去网上找,随便找一个都可以
filename = DownLoadUtils.getFileName(agent,filename);
response.setHeader("content-disposition","attachment;filename="+filename);