Servlet对象的生命周期
1、什么是生命周期?
生命周期表示个java对象从最初被创建到最终被销毁,经历的所有过程。
2、Servlet对象的生命周期是谁来管理的?程序员可以干涉吗?
Servlet对象的生命周期,java web程序员是无权干涉的,包括该Servlet对象的相关方法的调用,java web程序员也是无权干涉的。Servlet对象从最初的创建,方法的调用,以及最对象的销毁,整个过程,是由WEB容器来管理的。Web Container管理Servlet对象的生 命周期。
“默认情况下”,Servlet对象在WEB服务器启动阶段不会被实例化。【若希望在web服务器启动阶段实例化Servlet对象,需要进行特殊的设置】
3、描述Servlet对象生命周期
1)用户在浏览器地址栏上输入URL:localhost:8080/prj-servlet-03.index.html
2)web容器截取请求路径:/prj-servlet-03/testLifeCycle
3)web容器在容器上下文中找请求路径/prj-servlet-03/testLifeCycle对应的Servlet对象
4)若没有找到Servlet对象
4.1)通过web.xml文件中相关的配置信息,得到请求路径/testLifeCycle对应的Servlet完整类名
4.2)通过反射机制,调用Servlet类的无参数构造方法完成Servlet对象的实例化
4.3)web容器调用Servlet对象的init方法完成初始化操作
4.4)web容器调用Servlet对象的service方法提供服务
5)若找到对应的Servlet对象,web容器直接调用Servlet对象的service方法提供服务
6)web容器关闭的时候或着webapp重新部署的时候或者该servlet对象长时间没有用户再次访问的时候,web容器会将改servlet对象销毁,在销毁该对象之前,web容器会调用Servlet对象的destory方法,完成销毁之前的准备
4.总结:
Servlet类的构造方法只执行一次
Servlet对象的init方法只执行一次
Servlet对象的service方法,只要用户请求一次,则执行一次
Servlet对象的destory方法只执行一次
注意:
init方法执行的时候,Servlet对象已经被创建好了。
destory方法执行的时候,Servlet对象还没有销毁,即将被销毁
Servlet对象时单例,但是不符合单例模式,只能称为伪单例。真单例的构造方法是私有化的,tomcat服务器是支持多线程的。所以Servlet对象在但实实例多线程的环境下运行的。那么实例变量涉及到修改操作,那么这个Servlet对象一定会存在线程安全问题,不建议在Servlet对象中使用实例变量,尽量使用局部变量。
5. 启动时服务器创建对象
若希望在web服务器启动阶段实例化Servlet对象,需要在web.xml文件中进行相关设置:
在servlet标签中加入load-on-startup标签:自然数越小,优先级别高
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>pro-01</display-name>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.bjpowernode.hello</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
6.Servlet接口中的这些方法中编写什么代码?什么时候用这些方法?
1)无参构造方法【以后就不需要考虑构造函数了,尽量别动构造函数】
2)init方法
以上两个方法执行时间几乎是相同的,执行次数都是一次,构造方法执行的时候对象正在创建,init方法执行的时候对象已经创建:
若系统要求在对象创建时刻执行一段特殊的程序,这段程序尽量写到init方法中。
为什么不建议将代码编写到构造函数中呢?
存在风险!
当程序员编写构造方法的时候,可能会导致无参数构造方法不存在,一个类不编写任何构造函数,默认有一个无参数的构造方法,但是编写一个有参数的构造方法后,系统则不在提供无参数构造函数
Servlet中的init方法是SUN公司为java web程序员专门提供的一个初始化时刻,若希望给在初始化时执行一段特殊的程序,这个程序可编写到init方法,将来会被自动调用
3)service方法
这个方法是必然要重写的,因为在这个方法需要完成业务逻辑的处理,请求的处理,以及完成响应
而且这个代码中的方法是最具有价值的,也是最难写的,因为最难写的就是业务代码啦
4)destory方法
这个方法也是SUN公司为java web 程序员提供的一个特殊的时刻,这个特殊的时刻被称为对象销毁时刻,若希望销毁时刻执行一段特殊的代码,需要将这段代码编写到destory方法,自动被容器调用
ServletConfig对象详解
1.ServletConfig到底是什么?
ServletConfig是一个Servlet对象配置信息对象,ServletConfig对象中封装了一个Servlet对象的配置信息
Servlet对象的配置信息到web.xml文件中
一个Servlet对象对应一个ServletConfig对象,100个Servlet对象对应100个ServletConfig对象
2.ServletConfig接口中有哪些常用方法?
String getInitParameter(String name) :通过初始化参数的name获取value
xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>pro-01</display-name>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.bjpowernode.hello</servlet-class>
<!--初始化参数:被封装到ServletConfig对象中-->
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
java主要代码:
package com.bjpowernode;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class hello implements Servlet{
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
ServletConfig config = getServletConfig();
String user = config.getInitParameter("user");
System.out.println(user);
}
}
root
Enumeration getInitParameterNames() :获取所有初始化name的value
以上述方法中的xml文件基础:
java主要代码:
package com.bjpowernode;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class hello implements Servlet{
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
ServletConfig config = getServletConfig();
Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) {
String string = (String) names.nextElement();
String value = config.getInitParameter(string);
System.out.println(string+"="+value);
}
}
}
password=123456
user=root
**String getServletName() **:获取Servlet-name标签的name
ServletContext getServletContext() :获取ServletContext【Servlet上下文】对象
ServletContext接口
1. Servlet、ServletConfig、ServletContext之间的关系:
-
一个Servlet对应一个ServletConfig,100个Servlet对应100个ServletCconfig
-
所有的Servlet对应一个ServletContext对象
2. ServletContext到底是什么?什么时候被创建?什么时候被销毁?创建几个?
-
ServletContext被翻译为:Servlet上下文【Context一般翻译为上下文】
-
一个webapp只有一个web.xml文件,web.xml文件服务器启动阶段被解析
-
一个webapp只有一个ServletContext对象,ServletContext在服务器启动阶段被实例化
-
ServletContext对象在服务器关闭的时候会被销毁
-
ServletContext对应的是web.xml文件,是web.xml文件的代表
-
ServletContext是所有Servlet对象四周环境的代表【在同一个webapp中,所有Servlet对象共享同一个四周胡环境对象,该对象就是ServletContext】
-
所有的用户若想共享同一条数据,可以将这个数据放到ServletContext对象中
-
一般放到ServletContext对象中的数据是不建议涉及到修改操作的,因为ServletContext是多线程共享的一个对象,修改的时候会存在线程安全问题
3. ServletContext接口中的常用方法:
void setAttribute(String name,Object object) :向ServletContext中添加数据(map.put(key,value))
Object getAttribute(String name) :从ServletContext范围中读取数据(Object value = map.get(key)😉
void removeAttribute(String name) :移除ServletContext范围中的数据(map.remove(key)😉
String getInitparameter(String name) :根据context-param标签的名称获取值
Enumeration getInitParameterNames() : 获取web.xml中上下文对象中所有context-param标签中的名称
String getRealPath(String path) : 获取文件的绝对路径
HTTP协议
1.什么是HTTP协议?
超文本传输协议
浏览器和服务器之间的一种通讯协议
该协议是W3C负责制定的,其实本质上就是数据传送格式提前制定好了。浏览器和服务器都必须按照这种数据格式进行接收和发送。
2.你们使用HTTP协议版本号是多少?
HTTP1.1
3.HTTP协议包括几部分?
-请求协议:从Browser发送到Server的时候d采用的数据传送格式一响应协议:从Server发送到Browser的时候采用的数据传送格式
4.请求协议?
请求协议包括四部分:
-
请求行
-
消息报头
-
空白行
-
请求体
请求行: 请求方式URI协议版本号
空白行: 专门用来分离消息报头和请求体的
5.响应协议?
相应协议包括四部分:
- 状态行
- 响应报头
- 空白行
- 响应体
状态行: 协议版本号 状态码 状态描述信息
空白行: 分离响应报头和响应体的
响应协议中重点掌握状态码:
- 200响应成功正常结束
- 404资源未找到
- 500服务器内部错误
- …
6.GET请求和POST请求
什么情况下浏览器发送的请求是GET请求?什么情况下浏览器发送的是POST请求?
只有使用表单form,并且将form标签的method属性设置为method="post"才是POST请求方式,其余剩下的请求方式都是基于GET方式的
GET请求和POST请求有什么区别?
-
GET请求在请求行上提交数据,格式: url?name=value&name=value&name=value…这种提交方式最终提交的数据会显示到浏览器的地址栏上
-
POST请求在请求体中提交数据,相按全,提交格式: name=value&name=value&name=value…n这种提交方式最终不会再浏览器地址栏上显示
-
POST请求在请求体中提交数据,所以POST请求提交的数据没有长度的限制[POST可以提交大数据]
-
GET请求在请求行上提交数据,所以GET请求提交的数据长度有限制
-
GET请求只能提交字符串数据,POST请求可以提交任何类型的数据, 包括视频,所以文件上传必须使用POST请求提交。
-
GET请求最终的结果,会被浏览器缓存收纳。而POST请求最终的结果不会被浏览器缓存。
GET请求和POST请求应当如何选择使用?
- 有敏感资源,必须使用POST
- 传送数据不是普通字符串,必须使用POST
- 这个请求是为了修改服务器端的资源,使用POST
- GET请求多数情况下是从服务器中读取资源,这个读取的资源在短时间内是不会发生变化的,所以GET请求的最终结果浏览器缓存起来了
- POST请求是为了修改服务器端的资源,而每一次修改结果都是不同的,最终结果没有必要被浏览器缓存
HttpServletRequst接口
1.HttpServletReqest接口中的常用方法:
String getParameter(String name)
Map getParameterMap()
Enumeration getParameterNames()
String[] getPatameterValues(String name)
String getContextPath() 获取上下文路径,webapp的路径
String getMethod() 获取浏览器的请求方式
String getRequstURI() 获取URI
StringBuffer getRequestURL() 获取URL
String getServletPath() 获取Servlet PATH url-pattern
String getRemoteAddr() 获取客户端IP地址
void setAttribute(String name,Object o) 向request范围中存储数据
Object getAttribute(String name) 从request范围中读取数据
void removeAttribute(String name) 移除request范围中的数据
RequestDispatcher getRequestDispatcher(String path) 获取请求转发器,让转发器指向某个资源
HTTPSession session = request.getSession() 获取当前的session,获取不到则创建Session
HTTPSession session = request.getSession(true) 获取当前的session,获取不到则创建Session
HTTPSession session = request.getSession(false) 获取当前的session,获取不到则返回null
web系统的资源跳转
1.转发:
request.getRequestDispatcher(“url-pattern标签中的请求”).forward(request,response)
2.重定向:
response.sendRedirect(request.getContextPath()+“url-pattern标签中的请求”)
3.转发和重定向的相同点和不同点:
相同点:
都可以完成资源跳转
不同点:
转发是request对象触发的生
重定向是response对象触发的
转发是一次请求,浏览器地址栏上地址不会变化
重定向是两次请求,测览器地址栏上的地址发生变化
重定向的路径需要加webapp的报路径
转发是在本项目内部完成资源跳转
本重定向可以完成跨app跳转资源
Cookie
1. Cookie是什么?Cookie的作用?Cookie保存在哪里?
- 翻译过来:曲奇饼干
- Cookie可以保存会话状态,但是这个会话状态是保留在客户端上
- 只要Cookie清除,或者Cookie失效,这个会状态就没有了
- Cookie是保存在浏览器客户端上的
- Cookie可以保存在浏览器的缓存文件中,浏览器关闭Cookie消失
- Cookie也可以保存在客户端的硬盘文件中,浏览器关闭Cookie还在,除非Cookie失效
2. Cookie只有是在javaWeb中有吗?
- Cookie不只是在javaweb中存在
- 只要是web开发,只要是B/S架构的系统,只要是基于HTTP协议,就有Cookie的存在
- Cookie这种机制是HTTP协议规定的
3. Cookie实现的功能,常见的有哪些?
- 保留购物车商品的状态在客户端上
- 十天内免登录
- …
4. Java如何创建Cookie?
//创建Cookie对象
Cookie cookie1 = new Cookie("username","zhangsan");
Cookie cookie2 = new Cookie("password","123");
//设置Cookie的有效期
cookie1.setMaxAge(时间毫秒数);
//设置Cookie关联路径
Cookie1.setPath(request.getContextPath()+"/new")
//将Cookie对象发送给浏览器客户端
response.addCookie(cookie1);
response.addCookie(cookie2);
HttpSession
web编程中的Session:
-
Session表示会话,不只是在javaweb中存在,只要是web开发,都有会话这种机制。
-
在Java中会话对应的类型是:javax.servlet.http.HttpSession,简称session/会话
-
Cookie可以将会话状态保存在客户端,HttpSession可以将会话状态保存在服务器端
-
HttpSession对象是一个会话级别的对象,一次绘画对应一个Session对象
-
什么是一次会话?
可以这样理解:用户打开浏览器,在浏览器上发送多次请求,知道关闭浏览器,表示一次完整的会话
-
在会话的进行过程中,web容器维护了大量的HttpSession对象,换句话说,在web容器中应该有个“Session列表”
思考:为什么当前会话中的每一次请求可以获取到属于自己的会话对象?session的实现原理?
- 打开浏览器,浏览器上发送首次请求
- 服务器会创建一个HeepSession对象,该对象代表一次会话
- 同时生成HttpSession对象对应的Cookie对象,并且Cookie对象的name是JSESSIONID,Cookie的value是32位长度的字符串
- 服务器将Cookie的完整value和HttpSession对象绑定到session列表中
- 服务器将Cookie完整发送给浏览器客户端
- 浏览器客户端将Cookie保存到缓存中
- 只要是浏览器不关闭,Cookie就不会消失
- 当再次发送请求的时候,会自动提交缓存当中的Cookie
- 当服务器收到Cookie,验证该Cookie的name确实是:JSESSIONID,然后获取该Cooke的value
- 通过Cookie的value去session列表检索对应的HttpSession对象
浏览器禁用Cookie会出现什么问题?怎么解决?
- 浏览器禁用Cookie,则浏览器缓存中不在保存Cookie
- 导致在同一个会话中,无法获取到相应的会话对象
- 禁用Cookie后,每一次获取的会话对象都是新的
浏览器禁用Cookie以后,若还想拿到相应的Session对象,必须用URL重写机制,怎么重写URL:
在url地址后面+;跟上jsessionid=XXXXX
浏览器关闭后,服务器端对应的session对象会被销毁吗?为什么?
- 浏览器关闭之后,服务器不会销毁session对象
- 因为B/S架构系统是基于HTTP协议,而HTTP协议是一种无连接。无状态协议
- 什么是无连接/无状态?
- 请求的瞬间浏览器和服务器的通道是打开的,请求响应结束之后,通道关闭
- 这样做的目的是降低服务器的压力
Session对象在什么时候被销毁?
- web系统中引入了session超时的概念
- 当很长一段时间(这个时间可以配置)没用用户再次访问该session对象,词是session对象超时,web服务器自动回收session对象
如何配置这个时间:(默认是30分钟)
<session-config>
<session-timeout>120</session-timeout>
</session-config>
什么算是一次会话呢?
- 一般多数的情况下是这样描述的:用户打开浏览器,在浏览器上进行一些操作,然后将浏览器关闭,表示一次会话结束
- 本质上的描述:从session对象的创建,到最终session对象超时之后的销毁,这个才是真正意义上的完整会话
HttpSession常用的方法:
void setAttribute(String name,Object value)
Object getAttribute(String name)
void removeAttribute(String name)
void invalidate() 销毁Session
ServletContext、HttpSession、HttpSetvletRequest接口的对比:
- 以上都是范围对象:
ServletContext application 是应用范围,完成跨会话共享数据
HttpSession session 是会话范围,完成跨请求共享数据,但是这些请求必须在同一个会话当中
HttpServletRequst requst 是请求范围,完成跨Servlet共享数据,但是这些Servlet必须在同一个请求当中【转发】
- 三个范围的排序:
ServletContext >HttpSession >HttpServletRequst
- 使用原则:由小到大尝试,优先使用小范围
例如:使用成功之后,已经登录的状态需要保存起来,可以将登录成功这个状态保存到session对象中
登陆成功状态不能保存到request范围中,因为一次请求对应一个新的request对象
登陆成功状态也不能保存到application范围中,因为登录状态是属于会话级别的,不能所有用户共享
JSP是什么?
- JavaServer Pages
- 基于Java语言实现的服务器端页面
- JSP时Java EE规范之一
JSP文件通常存放在什么位置?
- JSP可以放到WEB-INF目录外
- 在实际的开发中,有很多项目是将JSP放到WEB-INF目录中,保护JSP
- WEB-INF目录中的数据是相对安全的
JSP的文件后缀是什么?
- 默认是.jsp
- 但是jsp的文件后缀也是可以修改的,通过修改CATALINA_HOME/conf/web.xml文件
JS和JSP的区别:
- JS:JavaScript,在运行浏览器中,和服务器没有关系,和Java也没有关系
- JSP:JavaServer Pages,运行在服务器中,JSP底层就是Java程序,运行在JVM中
JSP的执行原理?
-
浏览器上访问的路径虽然是以.jsp结尾,访问的时某个JSP文件,其实底层执行的时jsp对应的java程序
-
Tomcat服务器负责将.jsp文件翻译生成.java源文件,并且将java源文件编译生成.class字节码文件
-
访问jsp,其实底层还是执行了.class文件中的程序
-
Tomcat服务器内置了一个JSP的翻译引擎,专门负责翻译JSP文件,编译java源文件
-
index.jsp会被翻译生成index_jsp.java,编译生成index_jsp.class
-
index_jsp这个类继承了HttpJspBase,而HttpJspBase继承了HttpServlet
-
jsp就是Servlet,只不过是职责不同,JSP的强项是做页面展示
在jsp文件中编写的所有html、css、JavaScript,对于JSP来说只是普通的字符串。被翻译到:out.write(“翻译到这里”)
JSP文件修改之后不需要重新部署,也不需要重启服务器
JSP文件第一次访问的时候为什么非常慢?
- 启动JSP翻译引擎
- 需要一个翻译的过程
- 需要一个编译的过程
- 需要Servlet对象的创建过程
- init方法的调用
- service方法调用…
为什么第2+次访问JSP的时候非常快?
不需要重新翻译
不需要重新编译
不需要创建Servlet对象
直接调用Servlet对象的Service方法
JSP也是单实例多线程环境下运行的一个Servlet对象
JSP的声明语法格式:
<%–注释内容–%>
注释
<%代码块%>
运行在Servlet的service()方法中
<%!代码块%>
可以在Servlet类里面Service方法之外,
声明实例变量,静态变量,方法,静态语句块、构造函数…
声明块中不能直接编写Java语句,除非是变量的声明
< jsp:scriptlet > 代码片段< /jsp:scriptlet >
JS代码
注意:
<% out.print(i); %>等同于<%= i %>
关于JSP的九大内置对象:
什么是内置对象?
可以直接在JSP文件中使用的引用
九大内置对象有哪些?
内置对象名称 | 完整类名 |
---|---|
PageContext | javax.servlet.jsp.PageContext(页面范围) |
request | javax.servlet.http.HttpServletRequest(请求范围) |
session | javax.servlet.http.HttpSession(会话范围) |
application | javax.servlet.ServletContext(应用范围) |
out | javax.servlet.jsp.JspWriter(标准输出流) |
response | javax.servlet.http.HttpServletConfig(响应对象) |
config | javax.servlet.ServletConfig(Servlet配置信息对象) |
exception | java.lang.throwble(异常引用) |
page | java.lang.Object[page = this;](很少用) |
以上内置对象只能在service方法中直接使用,在其他方法中无法直接使用,可以间接使用
JSP的Page指令:
指令的作用,是指导JSP的翻译引擎如何翻译JSP的代码
JSP中共三个指令:
- page 页面指令
- include 包含指令
- taglib 标签库指令
指令的语法格式:
<%指令名属性名=属性值 属性名=属性值…%>
关于JSP的page指令,page指令中的常用属性
-
contentType 设置JSP的响应内容类型,同时在响应内容类型的后面也可以指定响应的字符编码方式
-
pageEncoding 设置JSP响应时的字符串编码方式
-
import 组织导入
-
session 设置当前JSP页面中是否可以直接使用session内置对象
- session=“true” 表示在当前的JSP中可以直接使用内置对象session,程序执行的时候获取当前的session对象,若获取不到则新建session对象
- session=“false” 表示在当前的JSP中不能直接使用内置对象session
-
errorPage 错误页面
当前JSP页面出错之后,要跳转的页面路径,需要该属性执行
-
isErrorPage 该页面是否是错误页面
-
isErrorPage = “false” 表示内置对象exception无法使用【缺省情况下是false】
-
isErrorPage = “true” 表示内置对象exception可以使用
-
-
isELIgnored 是否忽略EL表达式
include指令
<%@include file = “/hello.jsp” %>
1.a.jsp可以将b.jsp包含进来,当然被包含的资源不一定是jsp,也可能是其他的网络资源
2.include的作用:
在网页中有一些主体框架,例如:网页头、网页脚、这些都是固定不变的,我们可以将网页头、网页脚等固定不变的编写到某个JSP文件中,在需要页面使用include指令包含进来。
优点:
代码量少了
便于维护【修改一个文件就可以作用与所有的页面】
一个JSP中可以使用多个include指令
include实现原理:
- 编译器包含
- a.jsp包含b.jsp,底层共生成一个java源文件,一个class字节码文件,翻译期包含/编译期包含/静态联编
静态联编的时候,多个jsp中可以共享一个局部变量
因为最终翻译之后service方法只有一个
JSP中的动作:
语法格式:
<jsp:动作名 属性名=属性值 属性名=属性值…>< /jsp:动作名>
JSP中的6个动作:
< jsp:include > 动态包含
和上面include实现原理不同的是:a.jsp包含b.jsp,底层共生成两个java源文件,两个class字节码文件
< jsp:forward> 请求转发
< jsp:param> 设置请求参数
< jsp:useBean> 创建一个对象
< jsp:setProperty> 给指定的对象属性赋值
< jsp:getProperty> 取出指定对象的属性值