1、Servlet
Servlet 是 Server 与 Applet 的缩写,是服务端⼩程序的意思。使⽤ Java 语⾔编写的服务器端程序, 可以像⽣成动态的 WEB ⻚,Servlet 主要运⾏在服务器端,并由服务器调⽤执⾏, 是⼀种按照 Servlet 标准来开发的类。 是 SUN 公司提供的⼀⻔⽤于开发动态 Web 资源的技术。(⾔外之意:要实现 web 开发,需要实现 Servlet 标准)
Servlet 本质上也是 Java 类,但要遵循 Servlet 规范进⾏编写,没有 main()⽅法,它的创建、使⽤、 销毁都由 Servlet 容器进⾏管理(如 Tomcat)。(⾔外之意:写⾃⼰的类,不⽤写 main ⽅法,别⼈⾃动 调⽤)
Servlet 是和 HTTP 协议是紧密联系的,其可以处理 HTTP 协议相关的所有内容。这也是 Servlet 应⽤ ⼴泛的原因之⼀。 提供了 Servlet 功能的服务器,叫做 Servlet 容器,其常⻅容器有很多,如 Tomcat, Jetty, WebLogic Server, WebSphere, JBoss 等等。
2、Servlet的实现
1、继承HTTPServlet类
public class Servlet01 extends HttpServlet {
}
2、重写service方法
service放专门用来处理请求
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求
}
}
3、添加注解,设置访问路径
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求
}
}
3、Servlet的生命周期
servlet中没有main方法,不能独立运行,servlet的运行完全由servlet引擎来控制调度。
1、实例和初始化
servlet只有在第一次访问的时候才会创建实例,而且只会执行一次
当请求到达时,查找该servlet对象是否存在,不存在则创建实例并初始化。
2、就绪
可以多次调用执行
请求到达,则自动调用service方法,该方法在servlet的生命周期中可以多次调用
3、销毁
只会执行一次
服务器关闭,会自动销毁servlet实例
@WebServlet("/ser05")
public class Servlet05 extends HttpServlet {
/*
就绪/服务方法(处理请求)
系统方法,自动调用
当请求到达Servlet容器的时候自动调用该方法
可以重复调用
*/
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet05");
}
/*
初始化方法
系统方法,自动调用
当请求到达Servlet容器的时候,Servlet容器判断是否有该对象存在,不存在则自动创建并实例化
该方法只会被执行一次
*/
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("Servlet创建");
}
/*
销毁方法
系统方法,自动调用
当服务器关闭时或结束程序时自动调用该方法
方法只会执行一次
*/
@Override
public void destroy() {
System.out.println("Servlet销毁");
}
}
4、Servlet的工作流程
- WebClient向Servlet容器发出请求
- Servlet容器接收WebClient的请求
- Servlet容器创建HttpServletRequest对象,把webClient的请求信息封装到这个对象中
- Servlet容器创建HttpServletResponet对象
- Servlet容器调用httpServlet的service方法,将request和respone两个对象当作参数传递给HttpServlet
- HttpServle调用HttpServletRequest对象的有关方法获取请求信息
- HttpServle调用HttpServletResponet对象的有关方法生成响应数据
- Servle容器把HttpServlet的响应结果传递给WebClient
5、HttpServletRequest
HttpServletRequest对象,使用该对象获取WebClient传来的数据
5.1、常用方法
1、获取请求的完整的URL
// 获取请求的完整的URL
String url = req.getRequestURL()+"";
System.out.println("请求的完整的URL:"+url);
2、获取请求的URI
// 获取请求的URI
String uri = req.getRequestURI();
System.out.println("请求的URI:"+uri);
3、获取请求的站点名
// 获取请求的站点名
String webName = req.getContextPath();
System.out.println("请求的站点名:"+webName);
4、获取请求方式
// 获取请求方式
String method = req.getMethod();
System.out.println("请求方式:"+method);
5、获取请求的所有参数
// 获取请求的所有参数
String queryString = req.getQueryString();
System.out.println("请求的所有参数:"+queryString);
6、获取请求协议
// 获取请求协议
String protocol = req.getProtocol();
System.out.println("请求协议:"+protocol);
5.2、获取请求参数
不管前台传来什么数据,在servlet中都是字符串
1、getParameter
获取指定name的一个数据
String uname = req.getParameter("uname");
2、getParameterValues
获取指定name的多个数据,返回字符串数组
String[] hobbys = req.getParameterValues("hobby");
5.3、请求乱码问题
由于现在的 request 属于接收客户端的参数,所以必然有其默认的语⾔编码,主要是由于在解析过程 中默认使⽤的编码⽅式为 ISO-8859-1(此编码不⽀持中⽂),所以解析时⼀定会出现乱码。有两种方式。
1、setCharacterEncoding
设置前台传来的数据的编码
该方式只针对POST请求有效
//设置请求的编码格式UTF-8格式
req.setCharacterEncoding("UTF-8");
2、编码转换
这种方式对任何类型的请求都是有效的。
Tomcat8及其以上,Get请求不会乱码
String uname = new String(req.getParameter("uname").getBytes("ISo-8859-1"),"UTF-8");
5.4、请求转发
请求转发,是⼀种服务器的⾏为,当客户端请求到达后,服务器进⾏转发,此时会将请求对象进⾏保 存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始⾄终只有⼀ 个请求发出。
req.getRequestDispatcher("index.html").forward(req,resp);
5.5、request作用域
可以该对象可以在**一次请求**中传递数据
第一个参数为名字,第二个为要传递的值,值可以时任意类型
// 添加内容
req.setAttribute("name","zhangsan");
// 删除内容
req.removeAttribute("name");
// 获取内容
req.getAttribute("name")
6、HttpServletResponse
HttpServletResponse对象,使用该对象像前台相应数据
6.1、相应数据
在服务器接收到请求数据后,进行处理。需要将处理结果响应给前台
1、字符流
只能相应字符数据
Writer writer = resp.getWriter();
writer.write("Hello World!");
2、字节流
可以相应任意类型的数据
ServletOutputStream sos = resp.getOutputStream();
sos.write("ahh".getBytes());
两种相应方式只能选择其中的一个,不能同时使用
6.2、相应乱码问题
在响应中,如果响应的数据中有中文,就有可能会乱码。这是因为服务器响应的的数据,会经过网络传输,服务器有一种编码格式,在客户端(浏览器)也会有一种编码格式。如果两者编码不一样,就会出现乱码。
6.2.1、字符流
因为服务器默认编码格式是ISO-8859-1,所以相应中文,一定会出现乱码
处理方案1
// 设置服务器相应给浏览器的数据的编码格式
resp.setCharacterEncoding("utf-8");
// 添加响应头,告知浏览器要响应的数据类型和编码
resp.setHeader("content-type","text/html;charset=UTF-8");
resp.getWriter().write("<h1>啊哈哈</h1>");
处理方案2
// 设置相应类型和编码格式
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("<h1>啊哈哈</h1>");
6.2.2、字节流
对于字节流,由于是传输的字节,所有可以会乱码。如果客户端和服务器使用的是一种编码格式就不会乱码,如果不是一种编码格式就会乱码
处理方案1
// 添加响应头,告知浏览器要响应的数据类型和编码
resp.setHeader("content-type","text/html;charset=UTF-8");
resp.getOutputStream().write("<h1>啊哈哈</h1>".getBytes("UTF-8"));
处理方案2
// 设置相应类型和编码格式
resp.setContentType("text/html;charset=UTF-8");
resp.getOutputStream().write("<h1>啊哈哈</h1>".getBytes("UTF-8"));
6.3、重定向
重定向是客户端行为,在客户端访问服务器后,服务器接收数据后,进行相应,在相应的同时会给客户端一个地址,当客户端接收到服务器的相应后,会马上回服务器给的地址发起新的相应。重定向会发生两次请求。
重定向方式1
// 客户端接收到相应就会马上向index.jsp发起新的请求
resp.sendRedirect("index.jsp");
重定向方式2
// 设置响应码,307或者302都可以
resp.setStatus(307); // 302
// 添加响应头
resp.setHeader("location","ser05");
请求转发和重定向的区别
请求转发 | 重定向 |
---|---|
一次请求,request域的数据共享 | 两次请求,request域的数据不共享 |
服务端行为 | 客户端行为 |
地址栏不会改变 | 地址栏发生改变 |
只可以转发到本站单 | 可以重定向到任何地方 |
7、Cookie
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息, 所以其安全性也是很差的。例如常见的记住密码则可以通过 Cookie 来实现。
7.1、Cookie的创建和发送
new Cookie(“name”,“admin”):第一个参数是Cookie的Name,第二个参数是Value
// 创建cookie
Cookie cookie = new Cookie("name","admin");
//发送Cookie
resp.addCookie(cookie);
可以在客户端F12查看
7.2、Cookei的获取
cookie不能获取指定的一个,只能获取所有,然后循环遍历
// 获取Cookie,返回的是一个Cookie数组
Cookie[] cookies = req.getCookies();
// 非空判断
if (cookies!=null && cookies.length>0){
// 遍历Cookie
for (Cookie cookie:cookies){
System.out.println("name:"+cookie.getName()+"---value:"+cookie.getValue());
}
}
7.3、设置Cookie的到期时间
Cookie是有有效期的,默认关闭浏览器失效。可以通过setMaxAge方法设置有效期
1、负整数
负整数表示关闭浏览器就失效,默认也是关闭浏览器失效
// 负整数 关闭浏览器失效,默认值-1
Cookie cookie = new Cookie("name1","zhangsan");
cookie.setMaxAge(-1); // 关闭浏览器就失效
resp.addCookie(cookie);
2、正整数
正整数表示存活时间,单位是秒
// 正整数 存活时间 单位秒
Cookie cookie1 = new Cookie("name2","lisi");
// 存活30秒,30秒后就失效
cookie1.setMaxAge(30);
resp.addCookie(cookie1);
3、零
零表示删除一个Cookie,存活时间为0,也就自动失效了
// 零 删除Cookie
Cookie cookie2 = new Cookie("name3","wanger");
cookie2.setMaxAge(0);
resp.addCookie(cookie2);
7.4、Cookie的路径问题
可以给Cookie设置路径,设置哪些路径可以访问Cookie
1、当前服务器下的任何资源都能访问cookie
Cookie cookie1 = new Cookie("cookie1","cookie1");
cookie1.setPath("/");
resp.addCookie(cookie1);
2、当前项目下的任何资源都能访问cookie
Cookie cookie2 = new Cookie("cookie2","cookie2");
cookie2.setPath("/s04");
resp.addCookie(cookie2);
3、指定项目下的任何资源都能访问cookie
Cookie cookie3 = new Cookie("cookie3","cookie3");
cookie3.setPath("/s03");
resp.addCookie(cookie3);
4、指定项目下指定目录下的任何资源都能访问cookie
Cookie cookie4 = new Cookie("cookie4","cookie4");
cookie4.setPath("/s04/ser02");
resp.addCookie(cookie4);
7.5、Cookie的注意点
1、Cookie如果换了浏览器或者换了电脑将会失效
2、Cookie中name不能为中文
如果要使用中文,需要进行编码
name = URLEncoder.encode(name,"UTF-8");// 编码转换
3、Cookie的数量和大小是有限的,大小一般在4kb左右
4、Cookie中如果出现重名的将会覆盖掉之前的
8、HttpSession
对于服务器而言,每一个连接到它的客户端都是一个 session,servlet 容器使用此接口创建 HTTP 客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个 session 中,最重要的就是属性的操作。
session 无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的 session,因为每一个 session 只保存在当前的浏览器当中,并在相关的页面取得。
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过 request.getSession()方法,来获取当前会话的 session 对象。
sessio的底层是依赖Cookie实现的
8.1、Session对象的创建和获取
如果这一次会话中存在session,则获取session对象,反之创建一个新的session对象
// 获取session
HttpSession session = req.getSession();
8.2、Session的常用方法
// 获得SessionID
String id = session.getId();
System.out.println(id);
// 获取session的创建时间,返回的是一个时间戳
System.out.println(session.getCreationTime());
// 获取session的最后一次访问时间,返回的是一个时间戳
System.out.println(session.getLastAccessedTime());
// 是不是新创建的session
System.out.println(session.isNew());
8.3、Session域
1、添加Session数据
HttpSession session = req.getSession();
session.setAttribute("uname","ahh");
session.setAttribute("upwd","123456");
2、移除Session数据
session.removeAttribute("upwd");
3、获取session数据
返回的是一个Object
HttpSession session = req.getSession();
System.out.println(session.getAttribute("name"));
8.4、Session对象的销毁
session对象的销毁有五种情况
1、默认事件到期
session的默认事件是30分钟,如果30分钟内没有发起新的请求,就会失效,当然,时间可以修改。
可以在 Tomcat 中的 conf 目录下的 web.xml 文件中进行修改。
<!-- session 默认的最大不活动时间。单位:分钟。 -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
2、自己设置到期时间
setMaxInactiveInterval:这是到期时间,单位是秒
当然有设置也有获取,getMaxInactiveInterval获取失效时间
HttpSession session = req.getSession();
// 设置失效时间
session.setMaxInactiveInterval(15);
3、立即失效
session.invalidate();
4、关闭浏览器失效
session底层是依赖域cookie的,cookie默认就是关闭就浏览器就失效,所以session也是关闭浏览器就失效
5、关闭服务器失效
session是服务器端的对象,服务器关闭了,session也就失效了
9、ServletContext
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的 ServletContext 对象。
该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享; 第二、该对象中保存了当前应用程序相关信息。例如可以通过 getServerInfo() 方法获取当前服务器信息 ,getRealPath(String path) 获取资源的真实路径等。
9.1、获取ServletContext
context的获取方式有很多,比如以下几种常用的
1、通过request获取
// 通过req获取
ServletContext context1 = req.getServletContext();
2、通过session获取
// 通过session获取
ServletContext context2 = req.getSession().getServletContext();
3、通过ServletConfig获取
// 通过ServletConfig获取
ServletContext context3 = getServletConfig().getServletContext();
4、直接获取,只能在Servlet中使用
// 直接获取,只能在Servlet中使用
ServletContext context4 = getServletContext();
9.2、常用方法
// 获取当前服务器信息
System.out.println(context1.getServerInfo());
// 获取项目的真实路径
System.out.println(context1.getRealPath("/"));
9.2、Context域
该域对象储存的数据只要服务器不关闭不手动移除,就会一直有效。
ServletContext context = req.getServletContext();
1、添加
// 添加
context.setAttribute("uname","zhangsan");
context.setAttribute("upwd","123");
context.setAttribute("uage","20");
2、删除
// 删除
context.removeAttribute("uage");
3、获取
ServletContext context = request.getServletContext();
String name = (String) context.getAttribute("uname");
9.3、Servlet中的三大域对象
域对象 | 描述 |
---|---|
request | 在一次请求中有效,请求转发有效,重定向失效。 |
session | 在一次会话中有效,请求转发和重定向都有效 |
context | 整个项目中有效 |
10、文件的上传与下载
10.1、文件上传
前台页面
<%--
Created by IntelliJ IDEA.
User: 2025
Date: 2020/3/24
Time: 16:07
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<!--
multipart/form-data:设置这是一个要提交文件的表单
-->
<form action="uploadServlet" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="uname" placeholder="请输入姓名" autocomplete="off"><br>
文件:<input type="file" name="myfile"><br>
<button>提交</button>
</form>
</body>
</html>
后端实现
package cn.yanghuisen.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
/**
* @Version 1.0
* @Author
* @Date 2020/3/24 16:44
* @Desc MultipartConfig 标识该Servlet支持文件上传
* servlet将multipart/form-data的POST请求数据封装成part对象,通过part对上传的文件进行操作
*/
@WebServlet("/uploadServlet")
@MultipartConfig // 上传文件必须设置该注解
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置请求数据编码
req.setCharacterEncoding("UTF-8");
// 获取普通表单
String uname = req.getParameter("uname");
System.out.println(uname);
// 获取文件 servlet将multipart/form-data的POST请求数据封装成part对象
Part part = req.getPart("myfile");
String fileName = part.getSubmittedFileName();
System.out.println("文件名:"+fileName);
// 获取文件存放路径
String path = getServletContext().getRealPath("/");
System.out.println(path);
// 把文件保存到指定目录
part.write(path+"/"+fileName);
}
}
10.2、下载
1、前台超链接下载
当浏览器识别不出文件时就会自动调用下载
<a href="download/01-Servlet.zip">压缩包文件</a>
2、指定download下载
通过添加downlaod属性下载
<a href="download/abc.txt" download>文本文件</a>
<a href="download/download.jpg" download="abc.png">图片文件</a>
3、后台实现下载
前台页面
<%--
Created by IntelliJ IDEA.
User: 2025
Date: 2020/3/24
Time: 17:09
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="downloadServlet">
<input type="text" name="filename" placeholder="请输入要下载的文件的名字" autocomplete="off">
<button>下载</button>
</form>
</body>
</html>
后端代码
package cn.yanghuisen.servlet;
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.io.*;
import java.util.Arrays;
/**
* @Version 1.0
* @Author
* @Date 2020/3/24 18:28
* @Desc
*/
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置请求编码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String fileName = req.getParameter("filename");
if (fileName==null || "".equals(fileName.trim())){
Writer writer = resp.getWriter();
writer.write("文件名不能为空");
writer.close();
return;
}
// 获取路径
String path = req.getServletContext().getRealPath("/download/");
File file = new File(path+fileName);
// 文件是否存在,并且是一个文件
if (file.exists() && file.isFile()){
// 设置一个浏览器不识别的相应格式
resp.setContentType("application/octet-stream");
// 设置响应头信息
resp.setHeader("Content-Disposition","attachment;filename="+fileName);
// 获取输入流
InputStream is = new FileInputStream(file);
// 获取输出流
ServletOutputStream sos = resp.getOutputStream();
int len = 0;
byte[] bytes = new byte[1024];
while ((len = is.read(bytes))!=-1){
sos.write(bytes,0,len);
}
sos.close();
is.close();
}else {
Writer writer = resp.getWriter();
writer.write("文件不存在");
writer.close();
}
}
}