HTTP协议
浏览器中的书写格式
HTTP请求
Get请求
Post请求
格式
请求行
请求头1
请求头2
请求空体
请求体
HTTP响应
格式
状态行
响应头1
响应头2
响应空行
响应体
Servlet的实现
servlet的生命周期
先看与Servlet生命周期有关的三个方法:init(), service(), destroy(). Servlet生命周期可被定义为从创建
直到毁灭的整个过程。以下是三个方法分别对应的Servlet过程:
init():Servlet进行初始化;
service():Servlet处理客户端的请求;
destroy():Servlet结束,释放资源;
在调用destroy()方法后,Servlet由JVM的垃圾回首器进行垃圾回收。
init()方法
Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化在Servlet生
命周期中init()方法只被调用一次。
当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的
线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。
init()方法的定义如下:
public void init() throws ServletException {
// 初始化代码...
}
service()方法
service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来
自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查
HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用doGet()、doPost()等方法。
service()的定义如下:
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException{
// service()代码...
}
destroy()方法
destroy()方法只会被调用一次,在Servlet生命周期结束时被调用。destroy()方法可以让Servlet关闭数
据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用destroy()方法之后,Servlet对象被标记为垃圾回收。
destroy()方法的定义如下所示:
public void destroy() {
// 终止化代码...
}
总结:
- 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。
- 再次访问时,只会执行service()方法,不再执行init()方法。
- 在关闭Web容器时会调用destroy()方法。
创建WEB项目
实现Servlet
配置servlet的两种方式
1.通过web.xml文件配置servlet
<servlet>
<servlet-name>servlet01</servlet-name>
<servlet-class>com.ccl.servlet.servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet01</servlet-name>
<url-pattern>/ser.do</url-pattern>
</servlet-mapping>
2.通过注解配置servlet
注意:两种配置方法不能同时使用
创建servlet的三种方式
1.实现javax.servlet.Servlet接口,这个接口定义了servlet的生命周期,所有的方法都要实现
@WebServlet("/HelloServlet")
public class UserServlet 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 {
servletResponse.getWriter().print("<h1>hello servlet</h1>");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
2.继承javax.servlet.GenericServlet类
public class GenServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().print("<h1>hello GenericServlet</h1>");
}
}
3.继承javax.servlet.http.HttpServlet类,会根据请求的类型进行特殊的调用
public class HServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("<h1>hello HttpServlet</h1>");
}
}
servlet的匹配规则
精确匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user.html</url-pattern>
<url-pattern>/user.do</url-pattern>
</servlet-mapping>
在浏览器中输入如下几种url时,都会被匹配到该servlet
http://localhost:4444/servlet01/user.html
http://localhost:4444/servlet01/user.do
路径匹配 ,以"/"开头,并以"/*"结尾的字符串用于路径匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配
扩展名匹配,以"*"结尾的字符串用于扩展名匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
则任何扩展名为jsp或action的url请求都会匹配,比如下面的url都会被匹配
缺省匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
以任意字符开头,/结尾该servlet被匹配
匹配顺序
- 精准匹配
- 路径匹配,优先最长路径匹配
- 扩展名匹配
请求和响应
RERUEST
概述
request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。
request的功能可以分为以下几种:
- 封装了请求头数据;
- 封装了请求正文数据,如果是GET请求,那么就没有正文;
- request是一个域对象,可以把它当成Map来添加获取数据;
- request提供了请求转发和请求包含功能。(以后学习)
常用方法
方法名 | 描述 |
---|---|
getRequestURL() | 获取客户端发出请求时的完整URL |
getRequestURI() | 获取请求行中的资源名称部分(项目名称开始) |
getQueryString() | 获取请求行中的参数部分 |
getMethod() | 获取客户端请求方式 |
getProtocol() | 获取HTTP版本号 |
getContextPath() | 获取webapp名字 |
// 获取客户端请求的完整URL (从http开始,到?前面结束)
String url = req.getRequestURL().toString();
System.out.println("获取客户端请求的完整URL:" + url);
// 获取客户端请求的部分URL (从站点名开始,到?前面结束)
String uri = req.getRequestURI();
System.out.println("获取客户端请求的部分URL:" + uri);
// 获取请求行中的参数部分 (从?后面开始,到最后)
String queryString = req.getQueryString();
System.out.println("获取请求行中的参数部分:" + queryString);
// 获取客户端的请求方式
String method = req.getMethod();
System.out.println("获取客户端的请求方式:" + method);
// 获取HTTP版本号
String protocol = req.getProtocol();
System.out.println("获取HTTP版本号:" + protocol);
// 获取webapp名字(站点名)
String webapp = req.getContextPath();
System.out.println("获取webapp:" + webapp);
方法名 | 描述 |
---|---|
getParaMeter(name) | 获取指定名称的参数 |
getParamerterValues(String name) | 获取指定名称参数的所有值 |
setCharcterEncoding("UTF-8") | 针对POST乱码问题的处理方式 |
// 针对POST乱码问题的处理方式
req.setCharacterEncoding("UTF-8");
// 获取指定名称的参数,返回字符串
String uname = req.getParameter("uname");
System.out.println("uname的参数:" + uname);
// 获取指定名称参数的所有参数值,返回数组
String[] hobbys = req.getParameterValues("hobby");
System.out.println("获取指定名称参数的所有参数值:" + Arrays.toString(hobbys));
请求转发
请求转发表示由 多个Servlet共同来处理一个请求 。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("AServlet");
RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
rd.forward(request, response);
}
}
public class BServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BServlet");
}
}
request域方法
一个请求会建一个request对象,如果在一个请求中经历了多个Servlet,那么多个Servlet就可以使用
request来共享数据。
下面是request的域方法:
- void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,
- Object getAttribute(String name):用来获取request中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)request.getAttribute(“xxx”);,获取名为xxx的域属性;
- void removeAttribute(String name):用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
- Enumeration getAttributeNames():获取所有域属性的名称;
域方法通常在进行重定向时使用,多个servlet共享数据。
RESPONSE
概述
response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。
response对象的功能分为以下四种:
- 设置响应头信息;
- 发送状态码;
- 设置响应正文;
- 重定向;
响应正文
response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:
- PrintWriter out = response.getWriter():获取字符流,处理字符;
- ServletOutputStream out = response.getOutputStream():获取字节流,处理文件;
在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 字符输出流
*/
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
writer.flush();
writer.close();
/**
* 字节输出流
*/
ServletOutputStream out = resp.getOutputStream();
out.write("你好".getBytes());
out.write("<h2>你好</h2>".getBytes());
out.flush();
out.close();
}
}
响应乱码问题
* 字符流
* getWriter()
* 一定会乱码,应为服务器默认的解析编码时ISO-8869-1,该编码不支持中文
*
* 字节流
* getOutputStream()
* 可能乱码,当前服务器的编码与客户端的编码不一致时,会出现乱码
*
* 解决方案
* 1.设置服务端的编码格式
* 2.设置客户端的编码格式
* 总结:
* 设置客户端和服务端的编码格式保持一致,且支持中文
* 响应json格式的数据时,设置响应类型为application/json
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//方法一
//同时设置客户端和服务端的编码
resp.setContentType("text/html;charset=UTF-8");
//方法二
//设置服务端的编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端的编码格式
resp.setHeader("content-type","text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
writer.flush();
writer.close();
}
}
重定向
设置状态码重定向
响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。
因为重定向是通知浏览器再第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
///设置状态码重定向
resp.setStatus(302);
resp.setHeader("Location","http://www.baidu.com");
}
}
便捷重定向
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//便捷重定向
resp.sendRedirect("http://www.baidu.com");
//如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:
resp.sendRedirect("/serspn/ser04");
}
}
重定向小结
- 重定向是两次请求,请求转发是一次
- 重定向的URL可以是其他应用,不局限于当前应用;
- 重定向的响应头为302,并且必须要有Location响应头;
- 重定向就不要再使用response.getWriter()或response.getOutputStream()输出数据,不然可能会出现异常;
请求转发与重定向的区别
- 重定向是两次请求,转发是一个请求
- 重定向是浏览器的行为,请求转发是服务器行为
- 重定向浏览器的地址会发生改变,转发不会
- 重定向可以重定向到任何地址,转发只能在项目内转发
Cookie对象
Cookie的创建和发送
通过new Cookie("key" , "value");来创建一个Cookie对象,要想将Cookie随响应发送到客户端,需要先添加到response对象中,response.addCookie(cookie);此时该Cookie对象随着响应发送到客户端,在浏览器上可以看见
public class scookie01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建Cookie
Cookie cookie = new Cookie("name","zhangsan");
//发送cookie
resp.addCookie(cookie);
}
}
Cookie的获取
在服务器端只提供了一个getCookie()方法用来获取客户端回传的所有cookie组成的一个数组,如果需要获取单个cookie则需要通过遍历,getName()获取Cookie的名称getValue()获取Cookie值
public class SCookie02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (cookies != null & cookies.length > 0){
for (Cookie cookie : cookies){
System.out.println(cookie.getName());
System.out.println(cookie.getValue());
}
}
}
}
Cookie设置到期时间
默认当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过setMaxAge(int time);方法设定cookie的最大有效时间,以秒为单位
负整数
cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失
正整数
表示cookie对象可存活指定的秒数,当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间
零
表示cookie作废,如果原来浏览器已经保存了这个Cookie,那么可以通过setMaxAge(0)来删除这个Cookie
public class SCookie03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("aa","AAA");
// 负整数,表示浏览器关闭即失效
//cookie.setMaxAge(-1);
// 正整数,表示存活指定秒数
//cookie.setMaxAge(15);
// 零,表示删除cookie
cookie.setMaxAge(0);
//响应cookie
resp.addCookie(cookie);
// 删除已有cookie
Cookie cookie1 = new Cookie("name", null);
cookie1.setMaxAge(0);
resp.addCookie(cookie1);
}
}
Cookie的注意点
输入中文
public class SCookie04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = "姓名";
String value = "李青";
// 通过URLEncoder.encode()进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value);
// 创建Cookie对象
Cookie cookie = new Cookie(name, value);
// 发送Cookie对象
resp.addCookie(cookie);
}
}
同名问题
如果浏览器发送重复的Cookie会覆盖原有的Cookie
浏览器存放Cookie的数量
不同的浏览器对Cookie有限定,Cookie的存储是有上限的。Cookie是存储在客户端(浏览器)的,而且一般是由服务器创建和设定。后期结合Session来实现回话跟踪。
Cookie的路径
public class SCookie06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//当前项目下的资源可获取Cookie对象(默认不设置Cookie的path)
Cookie cookie = new Cookie("a1", "A1");
resp.addCookie(cookie);
// 当前服务器中,任意资源都可以访问
Cookie cookie1 = new Cookie("a2", "A2");
cookie1.setPath("/");
resp.addCookie(cookie1);
//指定目录下的资源可以获取Cookie对象
Cookie cookie2 = new Cookie("a3", "A3");
cookie2.setPath("/serspn/test/scok02");
resp.addCookie(cookie2);
//指定项目下的资源可获取Cookie对象(指定站点名)
Cookie cookie3 = new Cookie("a4", "A4");
cookie3.setPath("/ser");
resp.addCookie(cookie3);
}
}
HttpSession对 象
基本使用
public class Session01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Session对象(如果session对象不存在,则新建session对象;如果session存在,则获取session对象)
HttpSession session = req.getSession();
//获取session的标识符
System.out.println(session.getId());
//获取第一次访问时间
System.out.println(session.getCreationTime());
//获取最后一次访问时间
System.out.println(session.getLastAccessedTime());
//是否是新的session对象
System.out.println(session.isNew());
//设置session域对象(一次对话有效)
session.setAttribute("uname","admin");
//获取指定名称的session域对象
String uname = (String) req.getAttribute("uname");
//移除指定名称的session域对象
session.removeAttribute("uname");
}
}
session对象的销毁
默认时间到期
Tomcat中conf目录下的web.xml文件中进行修改
<session-config>
<session-timeout>30</session-timeout>
</session-config>
自己设定到期时间
通过session.setMaxInactiveInterval(int)来设定session的最大活动时间,单位为秒
//获取session对象
HttpSession session = req.getSession();
//设置session的最大活动时间
session.setMaxInactiveInterval(15);
//获取session的最大不活动时间
int time = session.getMaxInactiveInterval();
立刻失效
//销毁session对象
session.invalidate();
关闭浏览器
seesion的底层依赖cookie实现,cookie的有效时间为关闭浏览器,从而seesion在浏览器关闭时也会失效
关闭浏览器
关闭服务器意味着此次会话结束,数据共享结束
ServletContext对象
public class ServletContext extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request获取对象
javax.servlet.ServletContext servletContext = req.getServletContext();
//通过Session获取
javax.servlet.ServletContext servletContext1 = req.getSession().getServletContext();
//通过ServletConfig对象获取
javax.servlet.ServletContext servletContext2 = getServletConfig().getServletContext();
//直接获取
javax.servlet.ServletContext servletContext3 = getServletContext();
//常用方法
//获取服务器版本信息
String serverInfo = servletContext.getServerInfo();
System.out.println("获取服务器的版本信息:" + serverInfo);
//获取项目的真实路径
String realPath = servletContext.getRealPath("/");
System.out.println("获取项目的真实路径:" + realPath);
}
}
Servlet三大作用域
request域对象
再一次请求中有效,请求妆发有效,重定向有效
session域对象
在一次会话中有效,请求转发和重定向都有效,seesion销毁后失效
servletContext域对象
在整个应用程序中有效,服务器关闭后失效