一、Tomcat服务器
1.1 软件类型
C/S结构软件:基于客户端和服务端的软件,如QQ、微信等。
B/S结构软件:基于浏览器与服务器的软件,如OA系统、京东商城、淘宝网等等。
1.2 Web资源
web资源就是可以通过浏览器提供给外界访问的资源。例如:图片、视频、文件上传和下载等等。
web资源分为两种:静态Web和动态Web。
1)静态web资源的内容一般不会发生变化。而且它的数据都是保存页面上。如果要修改数据,那么就必须要修改源码。例如:图片、HTML页面、CSS文件、JavaScript文件等等。
2)动态Web资源的数据可以发生变化。而且它的数据一般都是保存数据库里面。而且一般都会提供一个后台的管理系统来维护这些数据。如果要修改数据,那么就可以通过后台管理系统来修改数据。
1.3 服务器
1.3.1 什么是服务器
服务器其实一款软件。任何计算机只要安装服务器软件,那么这台计算机就可以作为一台服务器。
1.3.2 自定义服务器
public static void main(String[] args) throws Exception {
// 服务端
ServerSocket server = new ServerSocket(9000);
// 等待用户连接
Socket socket = server.accept();
// 文件输入流
InputStream in = new FileInputStream("E:\\site\\1.html");
// 获取客户端输出流
OutputStream out = socket.getOutputStream();
byte[] b = new byte[1024];
int len = -1;
// 读取文件内容,并向客户端输出内容
while ((len = in.read(b)) != -1) {
out.write(b,0,len);
}
// 关闭资源
in.close();
socket.close();
}
1.3.3 服务器软件
WebLogic : Oracle公司的产品。
Websphere:IBM公司的产品。
JBoss:Redhat公司的产品。
以上三种服务器都是用于开发大型的分布式Web应用的J2EE技术。除了JBOSS服务器是免费以外,其他两种服务器都是收费的。
Tomcat:Apache公司的产品,支持JSP、Servlet、JDBC等J2EE技术。一般Tomcat服务器适用于中小型的Web项目开发。
1.4 Tomcat服务器
1.4.1 Tomcat服务器安装
第一步:把Tomcat的压缩包解压到指定的目录中;
第二步:配置JAVA_HOME环境变量,该环境变量的值就是JDK的安装目录的根路径。
第三步:启动Tomcat服务器;
进入Tomcat安装目录下的bin文件夹,双击startup.bat文件。如果在命令行中没有出现错误信息,那么就代表启动成功。
第四步:打开浏览器,输入http://localhost:8080回车。 如果看到Tomcat的控制台,那么就代表安装成功。
1.4.2 安装过程中可能遇到的问题
情况一:如果启动Tomcat服务器的时候出现以下提示信息,那么就代表该端口号已经被其他程序占用。那么就必须要先把这个应用程序关闭后,才能够重新启动Tomcat。
情况二:如果启动Tomcat的时候出现一闪而过的现象,那么就要检查是否配置了JAVA_HOME环境变量。
1.4.3 Tomcat目录结构
bin:保存Tomcat的命令,例如:启动Tomcat、关闭Tomcat的命令。
conf:保存了Tomcat服务器的配置信息。例如:server.xml。
lib:保存javaee程序相关的jar包;
logs:保存服务器运行过程中的日志信息;
temp:保存服务器运行过程中的临时文件;
webapps:保存所有运行的项目文件;(非常重要)
注意:如果URL地址没有写项目名,那么默认访问是webapps目录下的ROOT文件夹中的index文件。
work:保存JSP的翻译文件。
1.4.4 部署项目
方式一:在webapps目录下部署web项目
优点:部署方便。只需要把项目直接复制到webapps目录下,然后启动Tomcat服务器即可。
缺点:如果webapps目录下的项目数太多,会导致服务器的访问效率很慢。
方式二:配置context节点
该方式可以把项目部署到其他目录下。但是需要配置在server.xml文件中配置Context节点。通过Context节点告诉Tomcat服务器从哪里加载我们的项目。
path代表上下文路径。默认上下文路径就是项目的名字。
docBase:代表项目所在磁盘上的路径。
优点:灵活性较高,不受部署项目数量的影响。
缺点:每次添加新的项目,都需要重新启动Tomcat服务器。
(3)方式三:使用热部署方式部署项目
优点:每次部署项目的时候,不需要重启服务器。
实现步骤:
第一步:在conf/catalina/localhost目录下添加一个context文件;
注意:context文件的名字就是上下文名字。
第二步:编辑该文件。
注意:因为context文件的名字就是上下文名字,所以Context节点不需要指定path属性。
二、Servlet技术
2.1 什么是Servlet?
Servlet是Java Server的简称。它是用Java编写的服务器端应用程序。具有平台独立性,主要功能是与网页进行数据交互,生成动态Web内容。
2.2 编写Servlet程序
Servlet类必须要在Web项目中进行定义。如果要创建一个Web项目,必须要按照下面的结构来创建项目。
2.1.1 编写Servlet程序的步骤
第一步:定义一个类,该类需要继承GenericServlet父类;
第二步:重写该类的service方法;
public class HelloServlet extends GenericServlet {
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
response.getOutputStream().write("hello servlet".getBytes());
}
}
第三步:在web.xml文件中配置该Servlet(告诉浏览器我们编写的Servlet类的访问路径)
<servlet>
<!-- Servlet类的别名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- Servlet类的完整路径 -->
<servlet-class>com.site.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- 该Servlet-name节点的内容必须要与上面的servlet-name节点的内容一致 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 配置该Servlet的访问路径 -->
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
2.1.2 URL的写法
http:通用协议,浏览器与服务器之间通信默认使用http协议。
localhost:本地域名,对应着IP地址127.0.0.1。
9090:端口号,tomcat默认端口号为8080。如果端口号为80,可以不写。
/abc:上下文路径,默认与项目名相同。
/hello.do:访问资源路径,对应web.xml文件中Servlet-mapping下的url-pattern节点。
2.3 Servlet的访问过程
当浏览器访问服务器的时候,首先浏览器会根据主机名和端口号查找指定的服务器。如果找到后,那么服务器会根据URL的上下文路径查找webapps目录下是否存在该名字的项目。如果存在,那么服务器就会到该项目下的web.xml文件中查找是否存URL中的访问路径的url-pattern节点。如果找到之后,那么它就先获取该Servlet的完整类名,然后再通过反射技术创建该Servlet类的对象,并调用它的service方法。而且,服务器会把浏览器发送过来的一些参数封装到Request对象中并传入到service方法中。
2.4 使用Eclipse工具开发web项目
第一步:配置Web项目的运行环境。
1)选择window -> Preference -> Server -> Runtime Environment。
2)点击Add按钮,然后根据本地的服务器版本选择对应的服务器。
3)点击Next按钮后,然后选择Tomcat安装目录。
选择完后点击Finish即可。
第二步:在server视图创建一个服务器
1) 打开server视图(选择window -> show view -> server打开视图)。然后点击下面的链接添加一个新的服务器。
2) 点击添加,然后选择服务器的版本,以及服务器的运行环境,然后在点击Finish即可。
第三步:创建Web项目
1) 打开Package Explorer视图,新建一个Web项目。
2) 在src目录下定义一个Servlet类。
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getOutputStream().write("hello world".getBytes());
}
}
继承GenericServlet和继承HttpServlet的区别?
如果继承GenericServlet,那么就需要重写它的service方法;如果是继承HttpServlet,那么就需要重写doGet或doPost方法。HttpServlet是专门用来处理Http请求,因此在实际开发中,一般都是继承HttpServlet父类。
3)在web.xml文件中配置该Servlet类。
注意:在Eclipse工具中创建的Web项目,它的目录结构与手动在磁盘上创建的目录结构是不相同的。
第四步:把Web项目部署到服务器。
1)在servers视图中,鼠标右键选择Add and Remove。
2)把web项目添加到右边。然后点击Finish即可。
第五步:启动Tomcat服务器
在servers视图中鼠标右键服务器,然后选择Debug或Start启动服务器。
三、Http请求和响应
3.1 Http请求
一个Http请求包含以下三部分内容:请求行、请求头和请求正文。我们可以通过浏览器检查工具查看请求的相关信息,如下图所示:
请求行:包含了请求的基本信息。
请求头:包含了请求的一些参数信息。
请求正文:包含页面提交的表单数据。只有Post请求才有请求正文,Get请求是不存在请求正文。
3.1.1 请求行详解
请求行包含请求的一些基本信息。例如:请求地址、请求方式等等。
Request对象提供了访问请求行信息的方法:
- String getMethod():获取请求方式;
- String getRequestURL():获取请求的URL地址;
/*
获取请求行
*/
public class Servlet01 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求方式
String method = request.getMethod();
System.out.println("请求方式:" + method);
String url = request.getRequestURL().toString();
System.out.println("请求URL: " + url);
}
}
3.1.2 请求头
3.1.2.1 常见请求头
请求头:包含了请求的参数信息。如下图所示:
Accept:浏览器支持的文本格式;
Accept-Encoding:浏览器支持文本压缩格式;
Accept-Language:浏览器所支持的语言环境;
Cache-Control:跟浏览器缓存相关的请求头;
Connection:请求的连接方式;
Host:请求的主机名和端口号;
User-Agent:浏览器的版本信息;
3.1.2.2 获取请求头
Request对象提供了获取请求头的方法:
- String getHeader(String name):根据名字获取请求头;
- Enumeration getHeaderNames():获取所有请求头的名字;
/*
获取请求头
*/
public class Servlet02 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*String lang = request.getHeader("Accept-Language");
System.out.println("浏览器的语言环境:" + lang);*/
//获取所有请求头的名字
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
System.out.println(headerName + ":" + headerValue);
}
}
}
3.1.3 缓存问题
3.1.3.1 Get请求和Post请求的区别
1)Get请求的参数显示在地址栏上,而post请求的参数不会显示在地址栏上;
2)Get请求的参数记录在浏览器的历史记录上,而post请求不会把参数记录到历史记录中,因此,post请求的安全性比get请求高;
3)Get请求的提交数据的长度不能够超过2Kb,而post请求提交数据的长度没有限制;
4)如果是Get请求,浏览器默认会缓存数据;而浏览器不会对Post请求的数据进行缓存;
3.1.3.2 Get方式请求服务器中的静态资源
如果是get请求,第一次访问服务器的资源,那么服务器就通知浏览器缓存该资源。那么下次浏览器再请求服务器的时候就不会再从服务器中获取该资源,而是直接在本地的缓存文件夹读取该资源文件。
3.1.3.3 Get方式请求服务器中的动态资源
使用get方式请求服务器的动态资源,每次浏览器都需要从服务器中读取资源。为什么?
通过对HttpServlet类的源码进行分析,发现getLastModified方法总是返回-1。因此,浏览器请求服务器动态资源的时候,都会执行doGet方法。如果要让浏览器从本地缓存文件夹中读取数据,那么就需要重写HttpServlet的getLastModified方法。该方法需要返回资源文件的最后时间即可。
/*
获取请求头
*/
public class Servlet03 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//读取磁盘上的文件内容,然后把这些内容输出浏览器上。
FileInputStream fis = new FileInputStream(new File("d:/aa.txt"));
byte[] buf = new byte[5];
//读取文件中的内容
fis.read(buf);
//把内容输出到浏览器上
response.getOutputStream().write(buf);
}
@Override
protected long getLastModified(HttpServletRequest req) {
File f = new File("d:/aa.txt");
return f.lastModified(); //返回文件的最后修改时间
}
}
3.1.4 请求正文
请求正文保存了表单提交的数据。
Request对象提供了获取表单数据的方法:
- String getParameter(String name):根据参数名获取参数值;
- String[] getParameterValues(String name):根据参数名获取多个参数的值;
public class Servlet04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取姓名
String name = req.getParameter("name");
//获取年龄
String age = req.getParameter("age");
//获取兴趣爱好
String[] favors = req.getParameterValues("favors");
System.out.println("姓名:" + name + ",年龄:" + age + ",兴趣爱好:" + Arrays.toString(favors));
}
}
如果使用使用post方式提交数据给服务器,那么就有可能会出现乱码问题。
【原因分析】
因为浏览器发送数据给服务器之前,首先它会先使用页面的编码格式对数据进行编码,然后再发送给服务器。而服务器接收到浏览器发送过来的时候之后,首先服务器会使用request对象的默认码表ISO8859-1对这些数据进行解码。之所以出现中文乱码,是因为浏览器编码使用的码表和服务器解码所使用的码表不一致所造成的。
Request对象提供了处理Post请求中文乱码的办法:
- void setCharacterEncoding(String charsetName):设置Request对象的字符集。
public class Servlet04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//修改request对象的字符集
req.setCharacterEncoding("utf-8");
//获取姓名
String name = req.getParameter("name");
//获取年龄
String age = req.getParameter("age");
//获取兴趣爱好
String[] favors = req.getParameterValues("favors");
System.out.println("姓名:" + name + ",年龄:" + age
+ ",兴趣爱好:" + Arrays.toString(favors));
}
}
Get请求处理中文乱码的方法:
先把获取到的参数使用ISO8859-1进行编码,然后再使用UTF-8进行解码。
编码:使用字符串的getBytes方法。
解码:new String(“内容”, “字符集”)。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取姓名
String name = req.getParameter("name");
name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
//获取年龄
String age = req.getParameter("age");
//获取兴趣爱好
String[] favors = req.getParameterValues("favors");
System.out.println("姓名:" + name + ",年龄:" + age
+ ",兴趣爱好:" + Arrays.toString(favors));
}
3.2 HTTP响应
HTTP响应主要有响应头、响应正文和响应状态所组成。
响应头包含了服务器发送给浏览器的一些相关信息。
响应正文就是服务器发送给浏览器的内容。
3.2.1 响应状态
- 200:服务器响应成功。
- 302:告诉浏览器进行页面重定向(跳转其他页面);
- 304:告诉浏览器从缓存文件夹中读取数据;
- 404:访问的资源不存在;
- 500:服务器发生了异常;
3.2.2 常见的响应头
Response对象提供setHeader方法来设置响应头。
3.2.2.1 Content-Type头
Content-Type:告诉浏览器使用指定的字符集显示网页的内容。如果服务器给浏览器发送中文数据的时候,有可能会出现中文乱码。
【原因分析】
因为服务器发送数据给浏览器之前,首先它会使用response对象的默认字符集ISO8859-1对数据进行编码。而浏览器接收到服务器发送过来的数据之后,它就会使用默认的编码UTF-8进行解码。之所以出现中文乱码,就是因为服务器的编码格式与浏览器的编码格式不一致所造成。
【解决办法】
第一步:修改response对象的字符集为UTF-8;
第二步:设置Content-Type响应头;
public class Servlet05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
/*//修改response对象的字符集
resp.setCharacterEncoding("utf-8");
//设置响应头
resp.setHeader("Content-Type", "text/html;charset=utf-8");*/
//修改response对象的字符集,并且告诉浏览器使用该字符集显示内容
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("中文");
}
}
3.2.2.2 Content-Encoding头
- Content-Encoding:告诉浏览器使用指定的压缩格式对内容进行解压缩。
public class Servlet06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//响应头(告诉浏览器使用gzip格式来解压缩服务器发送过来的数据)
resp.setHeader("Content-Encoding", "gzip");
OutputStream out = resp.getOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(out);
for (int i = 0; i < 1000; i++) {
//sb.append("abcd");
gout.write("abcd".getBytes());
}
//把压缩后的数据写入底层输出流中
gout.finish();
}
}
3.2.2.3 Content-Disposition头
- Content-Dispostion:告诉浏览器以附件下载的方式打开数据。
public class ServletDemo02 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//告诉浏览器下载图片
response.setHeader("Content-Disposition", "attachment;filename=1.jpg");
//读取一张图片
FileInputStream fis = new FileInputStream("d:/1.jpg");
byte[] buf = new byte[1024];
int len = -1;
//把读取到的图片发送给浏览器
OutputStream out = response.getOutputStream();
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
//关闭资源
fis.close();
}
}
3.2.2.4 location头
- location:页面重定向的地址。
public class ServletDemo03 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置响应状态为302
response.setStatus(302);
//设置location响应头
response.setHeader("location", "http://www.baidu.com");
}
}
3.2.2.5 refresh头
作用:1)定时刷新页面;2)指定多少秒后跳转其他页面;
public class ServletDemo04 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//定时刷新网页
//response.setHeader("refresh", "1");
//指定多少秒后跳转其他页面
response.setHeader("refresh", "3;url=http://www.baidu.com ");
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String dateStr = sdf.format(d);
//处理响应的中文
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(dateStr);
}
}
四、Servlet的生命周期
Servlet是浏览器第一次请求该Servlet的时候创建出来。以后浏览器再次请求该Servlet的时候,服务器就不会再创建该Servlet,而是直接就使用之前创建的Servlet对象来处理请求。因此,一个Servlet对象只会创建一次。
Servlet生命周期的方法:
- init():创建servlet对象的时候,服务器会调用该Servlet的init方法。因此,该方法只会被调用1次。
- destory():当Servlet被卸载的时候,服务器就会调用该方法。该方法也是只会被调用1次。
- service():每次浏览器请求服务器的时候,都会调用该方法来处理请求。该方法会被调用多次。
一个Servlet对象在服务器中只会创建一次。一个Servlet对象是被多个用户所共享的。因此,不能够把用户相关的信息保存Servlet的成员属性里面。
@WebServlet("/demo05")
public class ServletDemo05 extends GenericServlet {
@Override
public void destroy() {
System.out.println("destory():该Servlet类被卸载的时候调用...");
}
@Override
public void init() throws ServletException {
System.out.println("init(): 创建Servlet对象的时候被调用...");
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
System.out.println("service(): 每次请求该Servlet的时候被调用...");
//获取用户名和密码
name = req.getParameter("name");
password = req.getParameter("pass");
//处理中文乱码问题
res.setContentType("text/html;charset=utf-8");
if ("jacky".equals(name) && "123".equals(password)) {
res.getWriter().write("<h1>登录成功!</h1>");
} else {
res.getWriter().write("<h1>用户名或密码不存在!</h1>");
}
}
}
五、配置多个路径
方式一:配置多个URL-PATTERN节点。
方式二:配置多个servlet-mapping节点。
方式三:使用通配符配置多个URL。
通配符*号代表可以匹配任意的路径。
注意事项:
1)星号通配符不能够乱用的,要么匹配后缀名,要么就匹配路径。
- 使用星号匹配后缀名。例如:*.do
- 使用星号匹配路径。例如:/abc/*
- 不能够又匹配后缀名,又匹配路径。例如:/abc/.do
2)如果不是在路径的前面使用星号,那么星号就不是通配符,而只是一个普通的字符。例如:/abc。
3)不要缺省路径。例如:/*或/。如果配置缺省路径,那么就无法访问项目中的静态文件。
六、Servlet内置对象
6.1 Request对象
Request对象封装了请求相关的信息。该对象是由容器负责创建,因此,可以在service或doXxx方法中直接使用。
Request对象的作用:1)获取请求头的信息;2)获取请求参数;3)实现请求的转发;4)作为域对象使用;
6.1.1 请求转发
第一步:创建请求分发器对象;
第二步:调用该对象的forward方法转发请求;
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//创建一个请求分发器对象
RequestDispatcher rd = req.getRequestDispatcher("/demo07/b");
//转发请求
rd.forward(req, resp);
//注意:如果执行了请求转发,那么就不能够再使用response向浏览器输出内容。
//resp.getOutputStream().write("ServetA..".getBytes());
}
}
注意:如果执行了请求转发,那么就不能够再使用response向浏览器输出内容。
6.1.2 作为域对象使用
域对象就是用来保存数据的对象,而且该对象可以实现不同Servlet之间的数据传递。
面试题:Servlet有哪些域对象?而且它们的作用范围是怎样的?
- PageContext:只能够在当前JSP页面中有效。
- Request:只在当前请求中有效;
- Session:在当前会话中有效;
- ServletContext:在当前web应用中有效;
域对象的方法:
- void setAttribute(String name, Object value):往域对象中添加数据;
- Object getAttribute(String name):从域对象中获取数据;
- void removeAttribute(String name):删除域对象中的数据;
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//向Request域对象添加数据
req.setAttribute("name", "小宝");
//创建一个请求分发器对象
RequestDispatcher rd = req.getRequestDispatcher("/demo07/b");
//转发请求
rd.forward(req, resp);
//注意:如果执行了请求转发,那么就不能够再使用response向浏览器输出内容。
//resp.getOutputStream().write("ServetA..".getBytes());
}
}
public class ServletB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = (String) req.getAttribute("name");
resp.getOutputStream().write(name.getBytes());
}
}
请求转发前和转发后其实是同一次请求。
6.1.3 请求转发和请求重定向的区别
请求重定向:
请求转发:
从上图的分析可以得出:
1) 请求转发浏览器只发送了一次请求,而请求重定向发送了两次请求。因此,请求转发的地址栏不会发生变化,而请求重定向的地址栏会发生改变;
2) 请求转发可以通过Request域对象来传递数据,而请求重定向不能够通过Request域对象传递数据。如果要通过请求重定向发送数据,那么只能够通过URL地址来传递数据。因此,如果执行页面跳转时候需要传递参数,那么就应该使用请求转发,否则就使用请求重定向;
3)请求转发不需要写项目名的,而请求重定向需要写项目名;
4)请求转发只能够访问项目里面的资源,而请求重定向可以访问站外资源;
6.2 ServletConfig对象
作用:获取Servlet的配置参数。
获取Servlet参数的步骤:
第一步:在web.xml文件的Servlet节点下,定义一个init-param节点。
第二步:在Servlet类中重写init(ServletConfig config)方法。并调用ServletConfig对象的getInitParameter()方法获取参数值;
6.3 ServletContext对象
6.3.1 作为域对象使用
Servlet对象的作用范围在整个web应用中都有效。在整个Web应用中只有一个ServletContext对象。每一个Servlet对象都可以获取到ServletContext对象。如果要获取该对象,可以通过Servlet对象或Request对象的getServletContext方法来获取。
@WebServlet("/demo01")
public class ServletDemo01 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取ServletContext对象
ServletContext sc = this.getServletContext();
sc.setAttribute("name", "小宝");
}
}
@WebServlet("/demo02")
public class ServletDemo02 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取ServletContext对象
ServletContext sc = this.getServletContext();
Object o = sc.getAttribute("name");
System.out.println(o);
}
}
因为ServletContext的作用域是在整个web应用中有效。因此,如果往ServletContext对象中存储数据,那么在所有的Servlet对象中都可以获取到该ServletContext中的数据。在实际使用中,该对象一般用来存储与项目相关的数据,例如:数据库配置信息。
6.3.2 获取上下文路径
在实际开发中,不要把上下文路径以硬编码方式写死在代码中。如果写死在代码中,那么如果在生产环境中上下文路径被修改了,那么我们就需要修改源码。所以,ServletContext对象提供了getContextPath()方法,用来获取项目的上下文路径;
6.3.3 获取项目中的资源文件
ServletContext对象提供了getRealPath和getResourceAsStream方法,它们可以根据资源文件的虚拟路径获取资源文件在磁盘上的绝对路径。
Request对象提供了获取资源文件的方法:
- String getRealPath(String path):根据资源文件的虚拟路径返回该文件在磁盘上的真实路径;
- InputStream getResourceAsStream(String path):把指定路径的资源文件读取到一个字节输入流中,并返回该输入流对象;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取ServletContext对象
ServletContext sc = this.getServletContext();
//动态获取images/Tesla.png在磁盘上路径
/*//方式一:获取该资源文件在磁盘上的绝对路径
String path = sc.getRealPath("/images/Tesla.png");
System.out.println("path = " + path);
//读取项目根路径下的images/Tesla.png图片
FileInputStream fis = new FileInputStream(path);*/
//方式二:获取资源文件的输入流
InputStream fis = sc.getResourceAsStream("/images/Tesla.png");
byte[] buf = new byte[1024];
int len = -1;
OutputStream out = response.getOutputStream();
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
fis.close();
}
注意:资源文件的虚拟路径必须要以反斜杠/开头,反斜杠代表web项目的根目录。
七、会话技术
7.1 什么是会话
会话是指从打开浏览器访问服务器开始,到关闭浏览器为止,这个过程就是一次会话。
7.2 Cookie技术
7.2.1 什么是Cookie?
Cookie其实就是保存在浏览器本地一个小文件,该文件保存了服务器中一些数据。每次浏览器请求服务器的时候,浏览器都会带着这个小文件的数据一起发送给服务器.
七天免登录的实现原理:
7.2.2 Cookie的基本操作
7.2.2.1 创建Cookie
第一步:创建Cookie对象;
Cookie cookie = new Cookie(“键”, “值”);
第二步:设置cookie的参数;
- setMaxAge():cookie的有效时间,单位是秒。如果没有设置有效时间,那么浏览器关闭的时候,cookie就会消失;
- setPath(String path):cookie的有效路径。例如:/taobao/demo01。如果设置了有效路径,那么只有该路径的请求才能够访问该Cookie。
第三步:把Cookie对象输出浏览器。
response.addCookie(cookie);
例如:
public class SetCookieServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//创建一个Cookie
Cookie c = new Cookie("name", "jacky");
//设置cookie的参数
//c.setMaxAge(0);
//设置有效路径
c.setPath("/day34/getCookie");
//把cookie写出浏览器
response.addCookie(c);
}
}
7.2.2.2 查询Cookie
第一步:调用request对象来获取cookie数组。
Cookie[] cookies = request.getCookies();
第二步:遍历cookie数组,然后调用cookie对象的方法获取cookie数据;
- getName():获取cookie对象的名字;
- getValue():获取cookie对象的值;
@WebServlet("/getCookie")
public class GetCookieServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取所有的cookie数组
Cookie[] cookies = request.getCookies();
System.out.println("cookies = " + cookies);
//遍历cookie数组,然后调用cookie对象的方法获取cookie数据;
for (Cookie c : cookies) {
System.out.println(c.getName() + "=" + c.getValue());
}
}
}
7.2.2.3 修改Cookie
创建一个同名的Cookie对象,然后再把该Cookie对象输出浏览器即可。
7.2.2.4 删除Cookie
第一步:创建一个同名的Cookie对象;
第二步:设置该Cookie对象的有效时间为0;
第三步:把cookie对象写出浏览器;
@WebServlet("/delCookie")
public class DeleteCookieServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//创建一个Cookie
Cookie c = new Cookie("name", "jacky");
//设置cookie的参数
c.setMaxAge(0);
//如果cookie包含有效路径,删除cookie的时候也要设置有效路径
c.setPath("/day34/getCookie");
//把cookie写出浏览器
response.addCookie(c);
}
}
注意:如果cookie包含有效路径,那么修改和删除cookie的时候也要设置有效路径。
7.3 Session技术
Session是基于cookie的服务端会话技术。
Session和Cookie的区别?
1) Cookie是客户端的会话技术,而Session是服务端的会话技术;
2) Cookie存储的数据可能会泄漏,因此Cookie的安全性比Session差;
3) Cookie保存的数据不能够超过4Kb,而Session没有大小的限制;
4) Cookie只能够存储字符串的数据,而Session可以存储任意类型的数据;
7.3.1 session的工作原理
浏览器第一次访问服务器的时候,服务器就会为每一个浏览器都会创建一个独享的Session对象。也就是说,每一个用户就对应着一个Session。因此,我们可以把用户的信息保存到Session对象。
7.3.2 Session的常用操作
Request对象提供了getSession方法,该方法用来获取session对象。
下面是Session对象的一些常用方法:
- void setAttribute(String name, Object value):往Session域中添加数据;
- Object getAttribute(String name):从Session域中获取数据;
- void removeAttribute(String name):从Sessioin中删除指定的属性;
- void invalidate():使session失效,相当于把Session中的数据都清除掉;
@WebServlet("/setSession")
public class SetSessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取Session对象
HttpSession session = request.getSession();
ArrayList<String> books = new ArrayList<String>();
books.add("java基础");
books.add("servlet高级编程");
books.add("SSH实战");
//往Sessioin中添加数据
session.setAttribute("books", books);
}
}
@WebServlet("/getSession")
public class GetSessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
//从Session对象中获取books属性值
List<String> books = (List<String>) session.getAttribute("books");
for (String book : books) {
System.out.println(book);
}
}
}
@WebServlet("/delSession")
public class DelSessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取Session对象
HttpSession session = request.getSession();
//删除Session中的books属性
//session.removeAttribute("books");
//使session失效
session.invalidate();
}
}
注意:如果浏览器关闭后,那么Session的数据就会被清空掉。
问题1:浏览器如何找到服务器中自己对应的Session?
Session也是基于cookie的技术。浏览器每次请求服务器的时候都会带着它的jsessionid一起发送给服务器。而服务器接收到该jsessionid之后就可以找到它对应的session了。
问题2:为什么浏览器关闭后Session就消失?
其实服务器中的Session对象并没有消失,而是浏览器中的jessionid的cookie已经消失。因为,该jsessionid是没有有效时间的。从而导致浏览器无法找到服务器中自己的Session对象。
问题3:如何在浏览器重启后,Session数据不消失?
修改cookie中jsessionid的有效时间,以及修改服务器中Session对象的超时时间。
因为每一个服务器中的Session对象也有一个有效时间。如果服务器中的Session对象超过了它的有效时间,那么服务器就会把该对象删除掉。该有效时间默认为30分钟。如果要修改该Session对象的有效时间,可以在web.xml文件中添加。例如:
问题4:如果服务器重启了,那么Session对象还在吗?
服务器关闭的时候,它会把Session中的数据保存到磁盘上。这个过程称为Session的钝化。服务器重新启动的时候,它就会从磁盘上把Session的数据重新读取回来。这个过程称为Session的活化。如果服务器重启后能够让Session中的对象恢复回来,那么保存在Session中的对象必须要实现序列化接口。