目录
@[TOC]
#创建并运行web项目
##1.Servlet的实现
新建java文件-->添加web项目-->重写Servlet方法-->Tomcat部署web项目-->运行服务器
注意:Servlet类继承HttpServlet类时IDE会报错,因为HttpServlet类在Tomcat/lib文件夹的servlet-api.jar包下。所以需要new一个project library,文件选到刚刚说到的servlet-api.jar包,然后新创建的类就可以正常继承HttpServlet啦。
##2.重写Servlet方法
满足 Servlet 规范只是让我们的类能够满足接收请求的要求,接收到请求后需要对请求进行分析,以 及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫做 service的方法,专门用来做 请求处理的操作,业务代码则可以写在该方法中。
##3.设置注解
在完成好了一切代码的编写后,还需要向服务器说明,特定请求对应特定资源。 开发servlet项目,使用@WebServlet将一个继承于javax.servlet.http.HttpServlet 的类定义为Servlet 组件。在Servlet3.0中 , 可以使用@WebServlet注解将一个继承于javax.servlet.http.HttpServlet的类 标注为可以处理用户请求的 Servlet。
##4.发布项目并启动服务
到此,需要编写和配置的地方已经完成,项目已经完整了,但是如果需要外界能够访问, 还需要将项 目发布到服务器上并运行服务器。
1. 设置项目的站点名(项目对外访问路径)
2. 设置项目的Tomcat配置
3. 启动服务器
##5.访问并查看结果
在项目正确发布到服务器上之后,用户即可通过浏览器访问该项目中的资源。注意 url 的 格式正确, tomcat 的端口为 8080。
#Servlet的工作流程
1. 通过请求头获知浏览器访问的是哪个主机
2. 再通过请求行获取访问的是哪个一个web应用
3. 再通过请求行中的请求路径获知访问的是哪个资源
4. 通过获取的资源路径在配置中匹配到真实的路径,
5. 服务器会创建servlet对象,(如果是第一次访问时,创建servlet实例,并调用init方法进行初始化 操作)
6. 调用service(request, response)方法来处理请求和响应的操作
7. 调用service完毕后返回服务器 由服务器讲response缓冲区的数据取出,以http响应的格式发送给浏览器
#Servlet的生命周期
Servlet没有 main()方法,不能独立运行,它的运行完全由 Servlet 引擎来控制和调度。 所谓生命周 期,指的是 servlet 容器何时创建 servlet 实例、何时调用其方法进行请求的处理、 何时并销毁其实例的 整个过程。
##实例和初始化时机
当请求到达容器时,容器查找该 servlet 对象是否存在,如果不存在,则会创建实例并进行初始 化。
##就绪/调用/服务阶段
有请求到达容器,容器调用 servlet 对象的 service()方法,处理请求的方法在整个生命周期中可以被 多次调用; HttpServlet 的 service()方法,会依据请求方式来调用 doGet()或者 doPost()方法。但 是, 这两个 do 方法默认情况下,会抛出异常,需要子类去 override。
##销毁时机
当容器关闭时(应用程序停止时),会将程序中的 Servlet 实例进行销毁。 上述的生命周期可以通过 Servlet 中的生命周期方法来观察。在 Servlet 中有三个生命周 期方法, 不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法 即可观察到 Servlet 的生命周期。
#Tomcat 与 Servlet 是如何工作的
1. Web Client 向 Servlet 容器(Tomcat)发出 Http 请求
2. Servlet 容器接收 Web Client 的请求
3. Servlet 容器创建一个 HttpServletRequest 对象,将 Web Client 请求的信息封装到这个对象 中 4. Servlet 容器创建一个 HttpServletResponse 对象
5. Servlet 容器调HttpServlet 对象service 方法,把 Request 与 Response 作为参数,传给 HttpServlet
6. HttpServlet 调用 HttpServletRequest 对象的有关方法,获取 Http 请求信息
7. HttpServlet 调用 HttpServletResponse 对象的有关方法,生成响应数据
8. Servlet 容器把 HttpServlet 的响应结果传给 Web Client
#HttpServletRequest对象
HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发 送的头信息等都属于客户端发来的信息,service()方法中形参接收的是 HttpServletRequest 接口的实例 化对象,表示该对象主要应用在 HTTP 协议上,该对象是由 Tomcat 封装好传递过来。
#接收请求常用方法
// 获取客户端请求的完整URL (从http开始,到?前面结束)
String url = req.getRequestURL().toString();
System.out.println("获取客户端请求的完整URL:" + url);
//获取请求时的部分路径(从项目站点名开始,到“?”前面结束
String uri=req.getRequestURI();
System.out.println("获取请求时的部分路径:"+uri);
//获取请求时的参数字符串(从“?‘后面开,到最后的字符串)
String queryString =req.getQueryString();
System.out.println("获取请求时的参数字符串:"+queryString);
//获取请求方法(Get和Post)
String method =req.getMethod();
System.out.println("获取请求方法:"+method);
//获取当前协议版本(Http/1.1)
String prototol =req.getProtocol();
System.out.println("获取当前协议版本:"+prototol);
//获取项目站点名(项目对外访问路径)
String webapp =req.getContextPath();
System.out.println("获取项目站点名:"+webapp);
//1.获取指定名称的参数值,返回字符串(重点)
String uname=req.getParameter("uname");
String upwd=req.getParameter("upwd");
System.out.println("uname="+uname);
System.out.println("upwd="+upwd);
//2.获取指定名称参数的所有参数值,返回字符串数组(用于复选框传值)
String[] hobbys=req.getParameterValues("hobby");
for(String hobby:hobbys)
System.out.println("爱好:"+hobby);
#解决请求乱码问题
方法一
req.setCharacterEncoding("UTF-8");
这种方式只针对 POST 有效(必须在接收所有的数据之前设定)
方法二
new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
借助了String 对象的方法,该种方式对任何请求有效,是通用的。 Tomcat8起,以后的GET方式请求是不会出现乱码的。
#请求转发
请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保 存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个 请求发出。
请求转发跳转的特点 1.服务端行为 2.地址栏不发生改变 3.从始至终只有一个请求 4.request数据可以共享
@WebServlet("/sr03")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname=req.getParameter("uname");
System.out.println("uname03="+uname);
req.getRequestDispatcher("sr04").forward(req,resp); //请求转发跳转
@WebServlet("/sr04")
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname=req.getParameter("uname");
System.out.println("uname04="+uname);
}
}
跳转后sr04也能接收(共享)到来自sr03的参数值
#request作用域
通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,即服务器跳转有效。
设置域对象内容
@WebServlet("/sr05")
public class Servlet05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet05...");
//设置域对象内容
req.setAttribute("name","admin");
req.setAttribute("age",18);
List<String> list=new ArrayList<>();
list.add("aaa");
list.add("bbb");
req.setAttribute("list",list);
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}
获取域对象内容
@WebServlet("/sr06")
public class Servlet06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet06...");
String name= (String) req.getAttribute("name");
System.out.println("name="+name);
Integer age= (Integer) req.getAttribute("age");
System.out.println("age="+age);
List<String> list= (List<String>) req.getAttribute("list");
System.out.println(list.get(1));
}
}
#响应数据
接收到客户端请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流。 有两种形式:
getWriter() 获取字符流(只能响应回字符)
getOutputStream() 获取字节流(能响应一切数据)
响应回的数据到客户端被浏览器解析。
注意:两者不能同时使用,只能相应一个请求
1.字符输出流
//getwrite 字符输出流(输出字符串)
//获取字符输出流
PrintWriter write=resp.getWriter();
write.write("Hello");
2.字节输出流
//getOutputStream() 字节输出流(输出一切数据)
ServletOutputStream out=resp.getOutputStream();
out.write("Hi".getBytes());
#解决响应乱码问题
同时设置服务端和客户端的编码格式
方法一:分别设置
//设置服务端的编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端的编码格式和响应的MIME类型
resp.setHeader("content-type","text/html;charset=UTF-8");
方法二:用一句同时设置
//同时设置客户端和服务端的编码格式
resp.setContentType("text/html;charset=UTF-8");
#重定向
重定向是一种服务器指导,客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会 进行响应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url);),当客户端接收到响应后,会立刻、马上、自动根据服务器给的新地址 发起第二个请求,服务器接收请求并作出响应,重定向完成。
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet04...");
/*重定向
* 服务端指导,客户端行为
* 存在两次请求
* 地址栏会发生改变
* request对象并不共享
*
* */
resp.sendRedirect("s05");
}
#注意区别请求转发和重定向
#Cookie对象
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行 处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服 务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息, 所以其安全性也是很差的。例如常见 的记住密码则可以通过 Cookie 来实现。
有一个专门操作Cookie的类 javax.servlet.http.Cookie。随着服务器端的响应发送给客户端,保存 在浏览器。当下次再访问服务器时把Cookie再带回服务器。
Cookie 的格式:键值对用“=”链接,多个键值对间通过“;”隔开。
##Cookie对象的创建和发送
通过 new Cookie("key","value");来创建一个 Cookie 对象,要想将 Cookie 随响应发送到客户端,需 要先添加到 response 对象中,response.addCookie(cookie);此时该 cookie 对象则随着响应发送至了 客户端。在浏览器上可以看见。
//创建Cookie
Cookie cookie=new Cookie("name","admin");
//发送Cookie
resp.addCookie(cookie);
##Cookie的获取
在服务器端只提供了一个 getCookies()的方法用来获取客户端回传的所有 cookie 组成的一个数组,如 果需要获取单个 cookie 则需要通过遍历,getName()获取 Cookie 的名称,getValue()获取 Cookie 的 值。
//Cookie的获取
//返回的是Cookie数组
Cookie[] Cookies = req.getCookies();
//遍历Cookie数组
if (Cookies != null && Cookies.length > 0) {
for (Cookie cookie : Cookies) {
System.out.println("name=" + cookie.getName());
System.out.println("value=" + cookie.getValue());
}
##Cookie设置到期时间
除了 Cookie 的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该 cookie 何 时失效。默认为当前浏览器关闭即失效。我们可以手动设定 cookie 的有效时间(通过到期时间计算), 通过 setMaxAge(int time);方法设定 cookie 的最大有效时间,以秒为单位。
到期时间的取值
负整数
若为负数,表示不存储该 cookie。 cookie 的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那 么 cookie 就会消失。
正整数
若大于 0 的整数,表示存储的秒数。 表示 cookie 对象可存活指定的秒数。当生命大于 0 时,浏览器会把 Cookie 保存到硬盘上,就算 关闭浏览器,就算重启客户端电脑,cookie 也会存活相应的时间。
零
若为 0,表示删除该 cookie。 cookie 生命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存 了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0)来删除这个 Cookie。 无论是在浏览器内 存中,还是在客户端硬盘上都会删除这个 Cookie。
/*
到期时间:
负整数(默认值是-1),表示只在浏览器存活,关闭浏览器就失效
0:表示删除该cookie
正整数:表示存活的秒数,会将数据存储到磁盘中
*/
Cookie cookie1=new Cookie("name1","jack");
cookie1.setMaxAge(-1);
resp.addCookie(cookie1);
Cookie cookie2=new Cookie("name2","micgo");
cookie2.setMaxAge(0);
resp.addCookie(cookie2);
Cookie cookie3=new Cookie("name3","Xboss");
cookie3.setMaxAge(60);
resp.addCookie(cookie3);
##Cookie的注意点
1. Cookie保存在当前浏览器中。 在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这 些信息就无效了。而且 cookie 还不能跨浏览器。
2. Cookie存中文问题 Cookie 中不能出现中文,如果有中文则通过 URLEncoder.encode()来进行编码,获取时通过 URLDecoder.decode()来进行解码。
String name = "姓名";
String value = "张三";
// 通过 URLEncoder.encode()来进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value);
// 创建Cookie对象
Cookie cookie = new Cookie(name,value);
// 发送Cookie对象
response.addCookie(cookie);
3. 同名Cookie问题 如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。
4. 浏览器存放Cookie的数量 不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器) 的,而且一般是由服务器端创建和设定。后期结合Session来实现回话跟踪。
情景一:当前服务器下任何项目的任意资源都可获取Cookie对象
情景二:当前项目下的资源可获取Cookie对象 (默认不设置Cookie的path)
情景三:指定项目下的资源可获取Cookie对象
情景四:指定目录下的资源可获取Cookie对象
Cookie cookie01=new Cookie("name01","admin");
cookie01.setPath("/");
resp.addCookie(cookie01);
Cookie cookie02=new Cookie("name02","micgo");
cookie02.setPath("/Sc01/c04");
resp.addCookie(cookie02);
Cookie cookie03=new Cookie("name03","NIU");
cookie03.setPath("/Sc01/c03");
resp.addCookie(cookie03);
Cookie cookie04=new Cookie("name04","cow");
cookie04.setPath("/Sc01");
resp.addCookie(cookie04);
总结:当访问的路径包含了cookie的路径时,则该请求将带上该cookie;如果访问路径不包含 cookie路径,则该请求不会携带该cookie
#HttpSession对象
HttpSession对象是 javax.servlet.http.HttpSession 的实例,该接口并不像 HttpServletRequest 或 HttpServletResponse 还存在一个父接口,该接口只是一个纯粹的接口。这因为 session 本身就属于 HTTP 协议的范畴。
对于服务器而言,每一个连接到它的客户端都是一个 session,servlet 容器使用此接口创建 HTTP 客 户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个 会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的 信息,比如会话标识符、创建时间和最后一次访问时间。在整个 session 中,最重要的就是属性的操作。
session 无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置 的 session,因为每一个 session 只保存在当前的浏览器当中,并在相关的页面取得。
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请 求)期间共享数据。我们可以通过 request.getSession()方法,来获取当前会话的 session 对象。
##标识符 JSESSIONID Session
既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是 sessionId。
每当一次请求到达服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端 回传一个名为 JSESSIONID 的 cookie,如果没有则认为这是一次新的会话,会创建 一个新的 session 对 象,并用唯一的 sessionId 为此次会话做一个标志。如果有 JESSIONID 这 个cookie回传,服务器则会根 据 JSESSIONID 这个值去查看是否含有id为JSESSION值的session 对象,如果没有则认为是一个新的会 话,重新创建一个新的 session 对象,并标志此次会话; 如果找到了相应的 session 对象,则认为是之 前标志过的一次会话,返回该 session 对象,数据达到共享。
这里提到一个叫做 JSESSIONID 的 cookie,这是一个比较特殊的 cookie,当用户请求服务器时,如果 访问了 session,则服务器会创建一个名为 JSESSIONID,值为获取到的 session(无论是获取到的还是 新创建的)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭 浏览器。 所以 Session 的底层依赖 Cookie 来实现。
##ServletContext对象
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的 ServletContext 对象。 该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享; 第二、该对 象中保存了当前应用程序相关信息。例如可以通过 getServerInfo() 方法获取当前服务器信息 , getRealPath(String path) 获取资源的真实路径等。、
###ServletContext对象的获取
获取 ServletContext 对象的途径有很多。
比如: 1. 通过 request 对象获取
ServletContext servletContext = request.getServletContext();
2. 通过 session 对象获取
ServletContext servletContext = request.getSession().getServletContext();
3. 通过 servletConfig 对象获取,在 Servlet 标准中提供了 ServletConfig 方法
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
4. 直接获取,Servlet 类中提供了直接获取 ServletContext 对象的方法
ServletContext servletContext = getServletContext();
常用方法
// 获取项目存放的真实路径
String realPath = request.getServletContext().getRealPath("/");
// 获取当前服务器的版本信息
String serverInfo = request.getServletContext().getServerInfo();
##Servlet的三大域对象
1. request域对象 在一次请求中有效。请求转发有效,重定向失效。
2. session域对象 在一次会话中有效。请求转发和重定向都有效,session销毁后失效。
3. servletContext域对象 在整个应用程序中有效。服务器关闭后失效。
#文件上传和下载
##文件上传
文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件, 这才是一个完整的文件上传。
前台页面
在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为 POST;其次我们的 form 表单的 enctype 必须设为"multipart/form-data",即 enctype="multipart/form-data",意思是设置表单的类型为文件上传表单。默认情况下这个表单类 型是 "application/x-www-form-urlencoded", 不能用于文件上传。只有使用了multipart/form-data 才 能完整地传递文件数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<!--
文件上传表单
1. 表单提交类型 method="post"
2. 表单类型 enctype="multipart/form-data"
3. 表单元素类型 文件域设置name属性值
普通的表单项 type="text"
文件项 type="file"
-->
<form method="post" action="upload" enctype="multipart/form-data">
姓名:<input type="text" name="uname" > <br>
文件:<input type="file" name="myfile" > <br>
<button type="submit">提交</button>
</form>
</body>
</html>
后台实现
使用注解 @MultipartConfig 将一个 Servlet 标识为支持文件上传。 Servlet 将 multipart/form-data 的 POST 请求封装成 Part,通过 Part 对上传的文件进行操作。
@WebServlet("/upload")
@MultipartConfig // 如果是文件上传表单,一定要加这个注解
public class UpLoad01 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
// 设置请求的编码格式
request.setCharacterEncoding("UTF-8");
// 获取普通表单项 (文本框)
String uname = request.getParameter("uname"); // "uname"代表的是文本框的name属性值
// 通过 getPart(name) 方法获取Part对象 (name代表的是页面中file文件域的name属性值)
Part part = request.getPart("myfile");
// 通过Part对象,获取上传的文件名
String fileName = part.getSubmittedFileName();
System.out.println(fileName);
// 获取上传文件需要存放的路径 (得到项目存放的真实路径)
String realPath = request.getServletContext().getRealPath("/");
System.out.println(realPath);
// 将文件上传到指定位置
part.write(realPath + fileName);
}
}
##文件下载
###超链接下载
<!-- 当超链接遇到浏览器不识别的资源时,会自动下载 -->
<a href="downLoad/hello.txt">文本文件</a>
<a href="downLoad/luffy.jpg">图片文件</a>
<a href="downLoad/1.rar">压缩文件</a>
<hr>
<!-- 当超链接遇到浏览器识别的资源时,默认不会下载。通过download属性可进行下载 -->
<a href="downLoad/hello.txt" download>文本文件</a>
<a href="downLoad/luffy.jpg" download="路飞.jpg">图片文件</a>
###后台下载
前端
<form action="downloadServlet" >
文件名:<input type="text" name="filename" placeholder="请输入要下载的文件">
<button>下载</button>
</form>
后端代码
@WebServlet("/downloadServlet")
public class download extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求的编码
req.setCharacterEncoding("UTF-8");
// 获取文件下载路径
String path = getServletContext().getRealPath("/downLoad/");
// 获取要下载的文件名
String name = req.getParameter("filename");
//参数的非空判断 trim():去除字符串的前后空格
if(name==null||"".equals(name.trim())){
resp.getWriter().write("请输入文件名");
resp.getWriter().close();
return;
}
// 通过路径得到file对象
File file = new File(path + name);
// 判断file对象是否存在,且是否是一个标准文件
if (file.exists() && file.isFile()) {
// 设置响应类型 (浏览器无法使用某种方式或激活某个程序来处理的类型)
resp.setContentType("application/x-msdownload");
// 设置头信息
resp.setHeader("Content-Disposition", "attachment;filename=" +
name);
// 得到输入流
InputStream is = Files.newInputStream(file.toPath());
// 得到输出流
ServletOutputStream os = resp.getOutputStream();
// 定义byte数组
byte[] car = new byte[1024];
// 定义长度
int len = 0;
// 循环 输出
while ((len = is.read(car)) != -1) {
os.write(car, 0, len);
}
// 关闭流 释放资源
os.close();
is.close();
} else {
System.out.println("文件不存在,下载失败!");
}
}
}