首页 > 其他分享 >Servlet进阶开发

Servlet进阶开发

时间:2023-03-26 18:01:21浏览次数:61  
标签:session 浏览器 进阶 Servlet 开发 Cookie import servlet javax


Servlet进阶

内容概述

Servlet进阶开发_java

Cookie和Session的引入

Cookie和Session技术的目的:为了解决http协议是无状态的。http协议无状态的意思是,浏览器发起请求(请求中一般是需要携带数据的),服务器接收到请求后调用相关的后端代码去处理该请求,处理完后会响应浏览器。 本次请求与响应结束后,相关的请求与响应数据就会销毁。

如果浏览器又发送了请求,而且本次请求需要用到上次请求传递过的数据,那么本次请求又要重新带上上次请求传递的数据。这样效率不高,而且用户体验度也差!

如何记住这些想要记住的信息呢?使用Cookie技术或者Session技术。

通过一个生活案例类比,来大致了解Cookie和Session的特点,后面再详细学习。

Servlet进阶开发_数据_02

Cookie对象

Cookie的引入

问题:

为了解决http协议是无状态的。

http协议无状态的意思是,浏览器发起请求(请求中一般是需要携带数据的),服务器接收到请求后调用相关的后端代码去处理该请求,处理完后会响应浏览器。 本次请求与响应结束后,相关的请求与响应数据就会销毁。

如果浏览器又发送了请求,而且本次请求需要用到上次请求传递过的数据,那么本次请求又要重新带上上次请求传递的数据。这样效率不高,而且用户体验度也差!

比如:用户在访问京东的时候,在登录界面输入用户名密码登录了。然后用户在结算的时候,因为http协议的无状态性,导致用户又得重新传递一次登录的信息才行!

解决:

浏览器在发起请求的时候,请求达到服务器,服务器认为本次请求携带的数据比较常用,以后的请求也会用得上,那么服务器就会在响应的时候告诉浏览器,把本次请求的数据给保存起来。然后浏览器以后每次发送请求访问服务器的时候都给带上!

总结:

  1. 服务器决定哪些数据是以后的请求也会用到的
  2. 服务器以响应的方式告诉浏览器将常用的这些数据存储起来,存储在浏览器端
  3. 浏览器以后每次发送请求的时候需要带上这些存储起来的数据

特点:

  1. Cookie是浏览器端的数据存储技术
  2. 浏览器每次发起请求的时候,请求信息中就包含了Cookie中存储的数据
  3. Cookie不适合大量数据的存储(每个Cookie存储的数据不超过4KB)
  4. 不安全,不适合存储重要的数据到浏览器端

实现:

Cookie技术。(Cookie的作用就是让浏览器保存数据的)

有一个专门操作Cookie的类 javax.servlet.http.Cookie,在服务器端创建,随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把Cookie再带回服务器。

Cookie的创建和发送

通过 new Cookie(“key”,“value”);来创建一个 Cookie 对象,要想将 Cookie 随响应发送到客户端,需要先添加到 response 对象中,response.addCookie(cookie);此时该 cookie 对象则随着响应发送至了客户端。在浏览器上可以看见。

案例:在Servlet中创建Cookie对象:

// 创建Cookie对象
Cookie cookie = new Cookie("uname","lili");
// 发送(响应)Cookie对象
response.addCookie(cookie);

查看Cookie源码发现:(以下为Cookie源码中截取的重点部分)

public class Cookie implements Cloneable, Serializable {
	private final String name;// name被final修饰,一旦复制就不能改变
    private String value;
	// 没有空构造器,只有一个有参构造器:
	public Cookie(String name, String value) {
        validation.validate(name);// 对name进行校验,确保name是唯一的标识
        this.name = name;// 给name赋值
        this.value = value;// 给value赋值
    }
}

访问创建Cookie对象的Servlet,F12 查看,发现浏览器中Cookie 的格式:键值对形式,用“=”连接

Servlet进阶开发_数据_03

Cookie的获取

在服务器端只提供了一个 getCookies()的方法用来获取客户端回传的所有 cookie 组成的一个数组,如果需要获取单个 cookie 则需要通过遍历,getName()获取 Cookie 的名称,getValue()获取 Cookie 的值。

PS:Cookie是浏览器的技术,如果你关闭了服务器,只要在浏览器中没有失效,Cookie就是一直携带的,可以发送请求到服务器获取Cookie信息。

// 获取Cookie数组
Cookie[] cookies = request.getCookies();
// 判断数组是否为空
if (cookies != null && cookies.length > 0) {
    // 遍历Cookie数组
    for (Cookie cookie : cookies){
        System.out.println(cookie.getName());
        System.out.println(cookie.getValue());
    }
}

再次请求发现:数据确实存储在浏览器中,并通过请求发送给服务器,服务器可以获取到Cookie的信息。

Servlet进阶开发_servlet_04

Cookie设置到期时间

除了 Cookie 的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该 cookie 何时失效。默认为当前浏览器关闭即失效。(可以自己测试一下)

我们可以手动设定 cookie 的有效时间(通过到期时间计算),通过 setMaxAge(int time);方法设定 cookie 的最大有效时间,以秒为单位。

到期时间的取值

  • 负整数
    若为负数,表示不存储该 cookie。
    cookie 的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。

Servlet进阶开发_数据_05

  • 正整数
    若大于 0 的整数,表示存储的秒数。
    表示 cookie 对象可存活指定的秒数。当生命大于 0 时,浏览器会把 Cookie 保存到硬盘上(PS:不同浏览器磁盘位置不同),就算关闭浏览器(PS:换浏览器不行,不能从谷歌换到火狐),就算重启客户端电脑,cookie 也会存活相应的时间。

  • 若为 0,表示删除该 cookie。
    cookie 生命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0)来删除这个 Cookie。 无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。

设置Cookie对象指定时间后失效

// 创建Cookie对象
Cookie cookie = new Cookie("uname","zhangsan");
// 设置Cookie 3天后失效  eg:之前做的登录账号密码 十天内免密登录 就是这样实现的
cookie.setMaxAge(3 * 24 * 60 * 60);
// 发送Cookie对象
response.addCookie(cookie);

可以在f12---->如下位置看到Cookie:

Servlet进阶开发_http_06

Cookie的注意点

  1. Cookie保存在当前浏览器中。
    不同的浏览器的cookie保存在磁盘上的位置是不同的,不能跨浏览器使用cookie。
    eg:在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了,跨浏览器也无效。
  2. 浏览器存放Cookie的数量
    不同的浏览器对Cookie也有限定,Cookie的存储有是上限的,,我们也不会去存储成千上万个Cookie,Cookie不适合大量数据的存储,每个Cookie存储的数据不超过4KB。Cookie是存储在客户端(浏览器)的,而且一般是由服务器端创建和设定。后期结合Session来实现回话跟踪。
  3. 同名Cookie问题
    如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。
  4. Cookie存中文问题
    Cookie 中不建议出现中文,如果有中文则通过 URLEncoder.encode()来进行编码,获取时通过 URLDecoder.decode()来进行解码。
    eg:一个Servlet中创建键为中文的Cookie并响应给浏览器:
String name = "姓名";
String value = "丽丽";
// 创建Cookie对象
Cookie cookie = new Cookie(name,value);
// 发送Cookie对象
response.addCookie(cookie);

报错:Cookie是不支持中文的

Servlet进阶开发_java_07

改变:

String name = "姓名";
String value = "丽丽";
// 通过 URL Encoder.encode()来进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value);
// 创建Cookie对象
Cookie cookie = new Cookie(name,value);
// 发送Cookie对象
response.addCookie(cookie);

eg:另一个Servlet中取出Cookie:

// 获取Cookie数组
Cookie[] cookies = request.getCookies();
// 判断数组是否为空
if (cookies != null && cookies.length > 0) {
    // 遍历Cookie数组
    for (Cookie cookie : cookies){
        // 获取时通过 URLDecoder.decode()来进行解码
        URLDecoder.decode(cookie.getName());
        URLDecoder.decode(cookie.getValue());
    }
}

Cookie的路径

Cookie的setPath设置cookie的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些cookie。

**情景一:**当前服务器下任何项目的任意资源都可获取Cookie对象

/* 当前项目路径为:s01 */
Cookie cookie = new Cookie("xxx","XXX");
// 设置路径为"/",表示在当前服务器下任何项目都可访问到Cookie对象
cookie.setPath("/");
response.addCookie(cookie);

**情景二:**当前项目下的资源可获取Cookie对象 (默认不设置Cookie的path)

/* 当前项目路径为:s01 */
Cookie cookie = new Cookie("xxx","XXX");
// 设置路径为"/s01",表示在当前项目下任何项目都可访问到Cookie对象
cookie.setPath("/s01"); // 默认情况,可不设置path的值
response.addCookie(cookie);

**情景三:**指定项目下的资源可获取Cookie对象

/* 当前项目路径为:s01 */
Cookie cookie = new Cookie("xxx","XXX");
// 设置路径为"/s02",表示在s02项目下才可访问到Cookie对象
cookie.setPath("/s02"); // 只能在s02项目下获取Cookie,就算cookie是s01产生的,s01也不能获取它
response.addCookie(cookie);

**情景四:**指定目录下的资源可获取Cookie对象

/* 当前项目路径为:s01 */
Cookie cookie = new Cookie("xxx","XXX");
// 设置路径为"/s01/cook",表示在s02/cook目录下才可访问到Cookie对象
cookie.setPath("/s01/cook"); 
response.addCookie(cookie);

HttpSession对象

Session是什么

session和cookie就是为解决HTTP协议的无状态采用的两种解决方案,称为会话跟踪技术。与cookie将信息保存在客户端不同,session将信息保存在服务器端解决。

PS : 一次请求,指的是客户端发送数据到服务器端,服务器端接收并相应,这可以称之为一次请求。由于HTTP协议是无状态的,你无法从一个请求中拿到另一个请求中的数据,多个请求之间的数据无法共享,那么就引入会话的概念。会话怎么理解呢?就好比你和我坐下来聊天谈话,你问我答,你问我答,你再问我前面的问题我还是可以回答你,整个会话聊天中的内容我们可以随时交谈使用。那我们这里说的会话(Session)指的是什么:

对于服务器而言,每一个连接到它的客户端,都产生一个会话 (Session),服务端为他们之间的会话创建一个session对象。servlet 容器使用 javax.servlet.http.HttpSession接口创建 HTTP 客户端和 HTTP 服务器之间的会话(构建HttpSession对象- Session在服务器端对应的实现是:HttpSession对象是 javax.servlet.http.HttpSession 的实例。)。Session是在服务器端生成的,存储在服务器端,即存在内存中。

Session生成的同时,会生成一个与之相关联的的SessionID, 此SessionID的存储是需要Cookie来完成的。SessionID是以名称为JSESSIONID、其值应该是一个既不会重复,又不容易被找到规律以仿造的字符串组成。SessionID会随着此次Http 响应,一并返回到客户端,并保存在客户端中。到当前请求再次发出后,该SessionID会随着Http头部,传到服务器中,服务器依据当前SessionID 得到与之对应的session。

一次会话(一个用户的多次请求)期间共享数据。我们可以通过 request.getSession()方法,来获取当前会话的 session 对象。

// 如果session对象存在,则获取;如果session对象不存在,则创建
HttpSession session = request.getSession();

案例:

在Servlet中获取Session内容:

package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/testsession01")
public class TestSession01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取Session对象:
        HttpSession session = req.getSession();
        // 获取Session的唯一标识符:
        String id = session.getId();
        System.out.println(id);
        // 断判是否是新的session对象:
        System.out.println(session.isNew());
    }
}

启动服务器:

注意:把这个勾选去掉,否则直接访问项目会产生Session会话,影响测试结果。

Servlet进阶开发_servlet_08

**第一次访问Servlet:**http://localhost:8888/servletdemo02/ts01

后端控制台结果:

Servlet进阶开发_java_09

浏览器中结果:

Servlet进阶开发_http_10

**第二次访问Servlet:**http://localhost:8888/servletdemo02/ts01

后台控制台:

Servlet进阶开发_数据_11

浏览器中结果:

Servlet进阶开发_java_12

你也可以请求其它页面,只要在同一个会话中,JSESSIONID都有效。(客户端请求服务器,会话即开启)

结论:

第一次访问发送请求到后端,第一次没有创建会话(Session)之前,那么由服务器创建一个HttpSession,并且将HttpSession对应的Sessionid绑定到http协议中响应回客户端。

第二次再访问,请求的时候在http请求协议中会在Cookie中携带Sessionid ,那么后台就会根据这个id取出对应的HttpSession。

测试,关闭服务器再重新测试,观察Sessionid的变化。

测试,关闭浏览器再重新测试,观察Sessionid的变化。

发现:一次会话(浏览器打开到关闭,访问多页页面,都在一个会话中)结束,结束后再次访问时,那么会创建新的Session。

PS:在浏览器Application中可以看到JSESSIONID:

Servlet进阶开发_servlet_13

标识符 JSESSIONID

Session 既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是 sessionId。

每当一次请求到达服务器,开启了会话,服务器第一步会查看是否从客户端回传一个名为 JSESSIONID 的 cookie,如果没有则认为这是一次新的会话,会创建 一个新的 session 对象,并用唯一的 sessionId 为此次会话做一个标志。如果有 JESSIONID 这 个cookie回传,服务器则会根据 JSESSIONID 这个值去查看是否含有id为JSESSION值的session 对象,认为是之前标志过的一次会话,返回该 session 对象,数据达到共享。

服务器在创建好Session对象后,服务器会将Session对象的id以Cookie的形式保存在客户端(浏览器),用户在发请求的时候,就会带上该Cookie,也就是SessionId到服务器,服务器会根据该id找到用户的Session对象。存储SessionId的Cookie不需要我们自己创建,我们在调用服务器完成Session创建的时候,Tomcat服务器会自动的创建Cookie,Cookie里面保存的是SessionId并响应给浏览器。但是注意,Cookie默认的有效期为浏览器运行期间,浏览器关闭,Cookie即失效。

不管是存储了用户SessionId的Cookie信息丢失,还是服务器存储的Session对象被销毁,(无论你是关闭浏览器还是关闭服务器,相当于会话断开了)默认只要服务器接收到用户发起的请求后,如果找不到对应的Session对象,都会重新创建,并将新的SessionId以Cookie的形式保存到浏览器中 。

session的使用

Session 用来表示一次会话,在一次会话中数据是可以共享的,这时 session 作为域对象存在(会话是一个域,在其中共享数据),可以通过setAttribute(name,value) 方法向域对象中添加数据,通过 getAttribute(name) 从域对象中获取数据,通过 removeAttribute(name) 从域对象中移除数据。

数据存储在 session 域对象中,当 session 对象不存在了,数据也就不能共享了。这就不得不谈到 session 的生命周期。

案例:

请求Servlet,将数据存于Session中(存两个,删一个),然后请求转发到另一个Servlet,从另一个Servlet中取出Session:

package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/ts02")
public class TestSession02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取Session对象:
        HttpSession session = req.getSession();
        // 在session域对象中存入内容:
        session.setAttribute("uname1","lili");
        session.setAttribute("uname2","feifei");

        // 移除指定名称的session域对象:
        session.removeAttribute("uname2");

        // 请求转发到另一个Servlet:
        req.getRequestDispatcher("/ts03").forward(req, resp);

    }
}
package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/ts03")
public class TestSession03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取Session对象:
        HttpSession session = req.getSession();
        // 获取指定名称的session域对象:
        String uname = (String)session.getAttribute("uname1");
        System.out.println(uname);
        String uname2 = (String)session.getAttribute("uname2");
        System.out.println(uname2);


    }
}

控制台结果:

Servlet进阶开发_http_14

session对象的销毁

默认时间到期

可以对生成的 Session 设置过期时间,如果不设置过期时间,默认的Session过期时间是30分钟(在不同的服务器中,它的过期时间略有不同,以Tomcat为案例来说)。

那么 session 的默认时间可以改么?答案是肯定的。可以在 Tomcat 中的 conf 目录下的 web.xml 文件中进行修改。

<!-- session 默认的最大不活动时间。单位:分钟。 -->
<session-config>
	<session-timeout>30</session-timeout>
</session-config>

当客户端第一次请求 servlet 并且操作 session 时,session 对象生成,Tomcat 中 session 默认的存活时间为 30min**(指的是这30min完全不操作),即你不操作界面的时间,一旦有操作,session 会重新计时。**

当然我们也可以通过 getMaxInactiveInterval() 方法来查看当前 Session 对象的最大不活动时间。

// 获取session的最大不活动时间
int time = session.getMaxInactiveInterval();

实际案例:在登录某个网站的时候,比如长时间不用以后,登录信息就过期了,你再操作就让你重新登录了,那就是因为登录数据都存在session中了,超过有效时间,session就消失了,那么就需要重新登录。

自己设定到期时间

当然除了以上的修改方式外,我们也可以在程序中自己设定 session 的生命周期,通过session.setMaxInactiveInterval(int) 来设定 session 的最大不活动时间,单位为秒。

// 获取session对象 
HttpSession session = request.getSession();
// 设置session的最大不活动时间
session.setMaxInactiveInterval(15); // 15秒

立刻失效

或者我们也可以通过 session.invalidate() 方法让 session 立刻失效

// 销毁session对象
session.invalidate();

代码:

package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/ts04")
public class TestSession04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取session对象:
        HttpSession session = req.getSession();
        // 立即失效:
        session.invalidate();
        // 设置session的最大不活动时间:
        //session.setMaxInactiveInterval(30);
        // 获取session的最大不活动时间:
        /*int time = session.getMaxInactiveInterval();
        System.out.println(time);*/
    }
}

关闭浏览器

从前面的 JESSIONID 可知道,session 依赖 cookie 实现,并且该 cookie 的有效时间为关闭浏览器,从而 session 在浏览器关闭时也相当于失效了(因为没有 JSESSION 再与之对应)。

关闭服务器

当关闭服务器时,session 销毁。

Session 失效则意味着此次会话结束,数据共享结束。

ServletContext对象

每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的 ServletContext 对象。

该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享; 第二、该对象中保存了当前应用程序相关信息。例如可以通过 getServerInfo() 方法获取当前服务器信息 ,getRealPath(String path) 获取资源的真实路径等。

ServletContext对象的获取

获取 ServletContext 对象的途径有很多。比如:

  1. 通过 request 对象获取
ServletContext servletContext = request.getServletContext();
  1. 通过 session 对象获取
ServletContext servletContext = request.getSession().getServletContext();
  1. 通过 servletConfig 对象获取,在 Servlet 标准中提供了 ServletConfig 方法
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
  1. 直接获取,Servlet 类中提供了直接获取 ServletContext 对象的方法
ServletContext servletContext = getServletContext();

ServletContext的使用

ServletContext 也可当做域对象来使用,通过向 ServletContext 中存取数据,可以使得整个应用程序共享某些数据。当然不建议存放过多数据,因为 ServletContext 中的数据一旦存储进去没有手动移除将会一直保存。

package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/tsc01")
public class TestServletContext01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletContext对象
		ServletContext servletContext = request.getServletContext();
		// 获取项目存放的真实路径
		String realPath = servletContext.getRealPath("/");
		// 获取当前服务器的版本信息
		String serverInfo = servletContext.getServerInfo();
		// 设置域对象
		servletContext.setAttribute("name","zhangsan");
    }
}

可以在项目的其它位置取出:(可以关闭服务器,因为不限于会话,是在整个服务器启动期间都好使的,对整个应用都好使。)

package com.msb.testsession;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/tsc02")
public class TestServletContext02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletContext对象
		ServletContext servletContext = request.getServletContext();
		// 获取域对象
		String name = (String) servletContext.getAttribute("name");
		// 移除域对象
		servletContext.removeAttribute("name");
    }
}

Servlet的三大域对象

认识域对象

什么是域对象
域对象类似于之前学习的map集合,可以存放键值对的数据。不同的是域对象中数据的使用有一定的区域范围限制。

三大域对象

request域 对应HttpServeltRequest对象 也叫请求域
session域 对应HttpSession对象 也叫会话域
application域 对应ServletContext对象 也叫应用域
常用API
setAttribute(key, value) 向域中添加/修改数据,无则添加,有则修改
getAttribute(key) 获取域中的数据
removeAttribute(key) 从域中移除指定key的数据

request域

有效范围
在一次请求内有效,比如:转发。一般用于存储单次请求之内的业务数据。
生命周期
创建:浏览器每次请求,服务器都会重新创建
使用:在请求的Servlet中或者请求转发后的Servlet中使用
销毁:请求响应结束后,该请求对象就会被销毁

Session域

有效范围

单次会话内有效,可以跨多个请求。
一般用来存储用户状态数据,比如用户的登录信息。

生命周期

  1. 创建:从第一次发出请求,会话开始
  2. 使用:本次会话内,浏览器和服务器之间发生的多次请求和响应都有效
  3. 销毁:会话结束,比如:达到最大不活动时间、手动清除、浏览器关闭

Application域

有效范围

当前web项目内都有效,可以跨请求,跨会话访问。
一般放一些全局的和项目本身相关的数据,如在线人数,不建议向这里放业务数据。

生命周期

  1. 创建:服务器启动
  2. 使用:服务器运行期间都有效
  1. 销毁:服务器关闭

文件上传和下载

在上网的时候我们常常遇到文件上传的情况,例如上传头像、上传资料等;当然除了上传,遇见下载的情况也很多,接下来看看我们 servlet 中怎么实现文件的上传和下载。

文件上传

实现案例:前台上传文件到服务器保存。

思路:文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件,这才是一个完整的文件上传。

前台页面

在做文件上传的时候,会有一个上传文件的界面,创建一个html页面即可:

Servlet进阶开发_服务器_15

首先我们需要一个表单,并且表单的请求方式为 POST;(必须)

其次我们的 form 表单的 enctype 必须设为"multipart/form-data",即 enctype=“multipart/form-data”,意思是设置表单的类型为文件上传表单。默认情况下这个表单类型是 “application/x-www-form-urlencoded”, 不能用于文件上传。只有使用了multipart/form-data 才能完整地传递文件数据。

upload.html中代码为:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
    <!--
    文件上传表单
        1. 表单提交类型 method="post"
        2. 表单类型 enctype="multipart/form-data"
        3. 表单元素类型  文件域设置name属性值
    -->
    <form method="post" action="uploadServlet" enctype="multipart/form-data">
      提交文件名字:<input type="text" name="filename" > <br>
      文件:<input type="file" name="uploadfile" > <br>
      <input type="submit" value="点击上传">
    </form>
</body>
</html>

后台实现

因为前台 enctype 设为"multipart/form-data",所以后台必须配套使用注解 @MultipartConfig ,才可以将一个 Servlet 标识为支持文件上传。,否则Servlet没有文件上传能力。Servlet 将 multipart/form-data 的 POST 请求封装成 Part对象,通过 Part对象对上传的文件进行操作。

package com.msb.upload;

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;

/**
 * @Author: zhaoss
 */
@WebServlet("/uploadServlet")
@MultipartConfig // 如果是文件上传表单,一定要加这个注解
public class UploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求的编码格式
        req.setCharacterEncoding("UTF-8");
        // 获取普通表单项 (文本框)
        String uname = req.getParameter("filename"); // "filename"代表的是文本框的name属性值
        // 通过 getPart(name) 方法获取Part对象 (name代表的是页面中file文件域的name属性值)
        Part part = req.getPart("uploadfile");
        // 通过Part对象,获取上传的文件名
        String fileName = part.getSubmittedFileName();
        // 将文件上传到指定位置
        part.write("D:/" + fileName);
        // 也可以上传到项目路径下: req.getServletContext().getRealPath("/") + fileName
    }
}

文件下载

文件下载,即将服务器上的资源下载(拷贝)到本地,我们可以通过两种方式下载。第一种是通过超链接本身的特性来下载;第二种是通过代码下载。

超链接下载

当我们在 HTML 或 JSP 页面中使用a标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的资源时会自动下载;当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如 txt、png、jpg 等。当然我们也可以通过 download 属性规定浏览器进行下载。但有些浏览器并不支持。

默认下载

<!-- 当超链接遇到浏览器不识别的资源时,会自动下载 -->
<a href="test.zip">超链接下载</a>

指定 download 属性下载

<!-- 当超链接遇到浏览器识别的资源时,默认不会下载。通过download属性可进行下载 -->
<a href="test.txt" download>超链接下载</a>

download 属性可以不写任何信息,会自动使用默认文件名。如果设置了download属性的值,则使用设置的值做为文件名。当用户打开浏览器点击链接的时候就会直接下载文件。

案例:利用超链接实现下载

在web下新建目录download,然后将资源粘贴进去:

Servlet进阶开发_http_16

需要注意的是:想要目录下资源被浏览器访问到,需要单独设置一下download目录,设置为资源路径:

Servlet进阶开发_服务器_17

Servlet进阶开发_服务器_18


Servlet进阶开发_服务器_19

编写代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <!--浏览器能够识别的资源-->
  <a href="download/hello2023.txt">文本文件</a>
  <a href="download/冰墩墩2023.png">图片文件</a>
  <!--浏览器不能够识别的资源-->
  <a href="download/test2023.rar">压缩文件</a>
  <hr>
  <a href="download/hello2023.txt" download>文本文件</a>
  <a href="download/冰墩墩2023.png" download="a.png">图片文件</a>
</body>
</html>

后台代码实现下载

实现案例:页面录入要下载的文件名,然后点击下载按钮进行下载。

前台实现:

录入文件名,点击下载按钮进行下载:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <form action="downloadServlet">
    文件名:<input type="text" name="filename" placeholder="请输入要下载的文件名">
    <input type="submit" value="下载">
  </form>
</body>
</html>

后台实现步骤

  1. 需要通过 response.setContentType 方法设置 Content-type 头字段的值, 为浏览器无法使用某种方式或激活某个程序来处理的 MIME 类型,例 如 “application/octet-stream” 或 “application/x-msdownload” 等。(设置这个以后,浏览器才会不识别文件而进行下载,否则就会直接识别文件,如文本、图片等)
  2. 需要通过 response.setHeader 方法设置 Content-Disposition 头的值 为 “attachment;filename=文件名” (设置这个以后,浏览器才会出现下载框并有下载文件名字)
  3. 读取下载文件,调用 response.getOutputStream 方法向客户端写入附件内容。
package com.msb.download;

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.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求信息的解码格式:
        req.setCharacterEncoding("UTF-8");
        // 设置响应信息的编码格式:
        resp.setCharacterEncoding("UTF-8");
        // 设置浏览器的编码格式:
        resp.setContentType("text/html;charset=UTF-8");
        // 获取文件名:
        String filename = req.getParameter("filename");
        // 获取资源的真实路径:
        String realPath = req.getServletContext().getRealPath("/download/");
        // 封装File对象:
        File file = new File(realPath + filename);

        // 判断文件file是否存在:
        if(file.exists()){
            // 下载:
            // 设置响应类型:
            resp.setContentType("application/octet-stream");
            // 设置头信息
            resp.setHeader("Content-Disposition", "attachment;filename=" + filename);
            // 得到输入流:
            FileInputStream fis = new FileInputStream(file);
            // 得到输出流:
            ServletOutputStream os = resp.getOutputStream();
            // 完成输入输出操作:边输入边输出
            int n = fis.read();
            while(n != -1){
                os.write(n);
                n = fis.read();
            }
        }else{
            resp.getWriter().write("文件不存在,下载失败!");
        }

    }
}

过滤器

介绍

Filter 即为过滤,用于在 Servlet 之外对 Request 或者 Response 进行修改。它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。

生活类比:饮水机过滤石


Servlet进阶开发_servlet_20

使用 Filter 的完整流程: Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再 对服务器响应进行后处理。

单个过滤器

Servlet进阶开发_服务器_21

多个过滤器

在一个 web 应用中,可以开发编写多个 Filter,这些 Filter 组合 起来称之为一个 Filter 链。(从一个过滤器将请求转发给下一个过滤器。)

Servlet进阶开发_数据_22


若是一个过滤器链:先配置先执行(请求时的执行顺序);响应时: 以相反的顺序执行。

在 HttpServletRequest 到达 Servlet 之前,拦截客户的请求 (HttpServletRequest )。根据需要检查HttpServletRequest,也可以修改 HttpServletRequest 头和数据。

在HttpServletResponse 到达客户端之前,拦截响应( HttpServletResponse)。根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse头和数据。

PS:框架中会学习拦截器,道理和这个类似。

实现

可以通过实现一个叫做javax.servlet.Fileter的接口来实现一个过滤器,其中定义了 三个方法,init(), doFilter(), destroy()分别在相应的时机执行。

Filter 的实现只需要两步:

Step1: 编写 java 类实现 Filter 接口,并实现其 doFilter 方法。

Step2: 通过@WebFilter注解设置它所能拦截的资源。

web 服务器在调用 doFilter 方法时,会传递一个 filterChain 对象进来,filterChain 对象是 filter 接口中最重要的一个对象,它提供了一个 doFilter 方法,开发人员可以根据需求决定 是否调用此方法,调用该方法,则 web 服务器就会调用 web 资源的 service 方法,即 web 资源就会被访问,否则 web 资源不会被访问。(本质是放行,调用doFilter方法后,即请求可以到达资源)

案例:编写过滤器,只拦截servlet01

在com.msb.filter包下编写过滤器:

package com.msb.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebFilter("/servlet01")
public class AFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("A过滤器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 放行前对请求做预处理:
        System.out.println("A过滤器执行-过滤请求");
        // 放行:放行以后才可以到达资源
        filterChain.doFilter(servletRequest,servletResponse);
        // 放行后对请求做后处理:
        System.out.println("A过滤器执行-过滤响应");
    }

    @Override
    public void destroy() {
        System.out.println("A过滤器销毁");
    }
}

在com.msb.servlet包下编写两个Servlet:

package com.msb.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/servlet01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet01中逻辑执行");
    }
}
package com.msb.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/servlet02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet02中逻辑执行");
    }
}

结果:

Servlet进阶开发_java_23

请求第一个Servlet,会经过过滤器:http://localhost:8888/s4/servlet01

Servlet进阶开发_servlet_24

请求第二个Servlet,不会经过过滤器:http://localhost:8888/s4/servlet02

Servlet进阶开发_服务器_25

停止服务器:

Servlet进阶开发_servlet_26

案例:实现过滤器链

加入过滤器B,B中拦截所有资源:

package com.msb.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebFilter("/*")
public class BFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("B过滤器初始化");
    }

    @Override
    public void destroy() {
        System.out.println("B过滤器销毁");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 放行前对请求做预处理:
        System.out.println("B过滤器执行-过滤请求");
        // 放行:放行以后才可以到达资源
        filterChain.doFilter(servletRequest,servletResponse);
        // 放行后对请求做后处理:
        System.out.println("B过滤器执行-过滤响应");
    }
}

访问servlet01,会经过过滤器A和B:

Servlet进阶开发_数据_27

过滤器链,请求时:先配置的先执行(这里的先配置指的是字母在前的过滤器),响应时:反过来。

过滤器应用案例

案例1-中文乱码处理

以往操作:在每个Servlet中都要加请求和响应乱码的解决代码,以防止中文乱码问题:

// 设置请求信息的解码格式:
req.setCharacterEncoding("UTF-8");
// 设置响应信息的编码格式:
resp.setCharacterEncoding("UTF-8");
// 设置浏览器的编码格式:
resp.setContentType("text/html;charset=UTF-8");

有了过滤器以后,不用每个Servlet都加上乱码解决代码了,只要经过过滤器就可以了,编写过滤器:

package com.msb.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebFilter("/*")
public class EncodeFilter implements Filter {


	public void destroy() {		
	}

	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		// 设置请求信息的解码格式:
        req.setCharacterEncoding("UTF-8");
        // 设置响应信息的编码格式:
        resp.setCharacterEncoding("UTF-8");
        // 设置浏览器的编码格式:
        resp.setContentType("text/html;charset=UTF-8");
		
		// 放行资源
		chain.doFilter(req, resp);		
	}

	public void init(FilterConfig fConfig) throws ServletException {
		
	}

}

Servlet中:

package com.msb.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/servlet03")
public class Servlet03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接收请求参数:(中文)
        String uname = req.getParameter("uname");
        System.out.println(uname);
        // 响应数据给页面:(中文)
        resp.getWriter().write("你好");
    }
}

案例2-用户非法访问拦截

等JSP学完以后,在登录案例中讲解。请查看JSP笔记中:

Servlet进阶开发_数据_28

监听器

监听器的应用场景

实际生活中是存在监听器的,比如:某个饭店的摄像头。可以随时观察每个顾客的动向,监听对象:去吃饭的人。

在web项目中,也有监听器,监听对象:三大域对象request、session、application。三大域对象作为数据流转的载体,但是这些域对象都不是我们创建的,也不是我们销毁的。

我们可以给三大域对象添加监听,监听三大域对象的创建及销毁,在监听到他们有动态的时候,执行一些方法。这就是监听器。

监听器的介绍

作用:

监听三大域对象的销毁、创建及数据资源的变更。

特点:

  1. 监听方法由tomcat根据监听结果来调用执行
  2. 监听方法中的逻辑代码由我们根据需要编写

监听器种类:(三类共八个监听器)

Request

ServletRequestListener           (监听request对象创建和销毁) 
ServleRequestAttributeListener   (监听域对象中的数据添加 替换 删除)

Session

HttpSessionListener              (监听session对象创建和销毁)
HttpSessionAttributeListener     (监听session域对象中的数据添加 修改 删除)
HttpSessionBindingListener       (监听session对象监听器绑定和解绑定接口)
HttpSessionActivationListener     (监听session对象钝化和活化状态接口)

Application

ServletContextListener            (监听application对象创建和销毁)
ServletContextAttributeListener   (监听application域对象中的数据添加 修改 删除)

PS:监听器的使用频率已经不高了,学习过程可以弱化这部分。所以你只要了解一下监听器是什么,能做什么,能实现什么效果,做个小案例练习一下即可。

监听器的使用

监听器的使用步骤:

  1. 创建一个类,实现监听器接口(Listener)
  2. 实现里面的方法
  3. 加入注解@WebListener配置监听器才会生效

案例:监听session的生命周期。

监听器:

package com.msb.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * @Author: zhaoss
 * 想要监听session的生命周期,那么需要实现session的监听接口
 * 并重写两个方法:sessionCreated,sessionDestroyed
 */
@WebListener
public class SessionListener implements HttpSessionListener {
    /**
     * session被创建的时候调用
     * @param se
     */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session被创建了");
    }

    /**
     * session被销毁的时候调用
     * @param se
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("session被销毁了");
    }
}

两个Servlet一个创建session一个销毁session:

package com.msb.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/servlet04")
public class Servlet04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取session:
        HttpSession session = req.getSession();
        System.out.println("session对象被创建了");
    }
}
package com.msb.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: zhaoss
 */
@WebServlet("/servlet05")
public class Servlet05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取session:
        HttpSession session = req.getSession();
        session.invalidate();
        System.out.println("session对象被销毁了");
    }
}

访问两次servlet04,访问一次servlet05,结果:

Servlet进阶开发_服务器_29

监听器应用案例

**案例:**网站在线人数的监控

**逻辑:**新过来一个用户,会创建新的session,当有新的session创建的时候,在线人数+1,当有session对象销毁的时候,在线人数-1。人数变量存在application作用域中,这样所有的会话请求(不同用户)都可以取出在线人数。

实现步骤:

Step1:创建一个监听器,需要实现某种接口,根据需求选取 HttpSessionListener ,用来检测 Session 的创建和销毁。

Step2:通过@WebListener注解配置该监听器

  1. 编写监听器:在类中定义一个成员变量用来存储当前的 session 个数。(OnlineListener.java)
/**
 * 在线人数统计
 * 	当有新的session对象被创建,则在线人数+1;
 * 	有session对象被销毁,在线人数-1;
 * @author zhaoss
 *
 */
@WebListener
public class OnlineListener implements HttpSessionListener {
	
	// 默认在线人数
	private Integer onlineNumber = 0;

	/**
	 * 当有新的session对象被创建,则在线人数+1;
	 */
	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// 人数+1
		onlineNumber++;
		// 将人数存到session作用域中
		// se.getSession().setAttribute("onlineNumber", onlineNumber);
		// 将人数存到application作用域中
		se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber);
	}

	/**
	 * 有session对象被销毁,在线人数-1;
	 */
	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// 人数-1
		onlineNumber--;
		// 将人数存到session作用域中
		// se.getSession().setAttribute("onlineNumber", onlineNumber);
		// 将人数存到application作用域中
		se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber);
	}

}

2.编写Servlet:做一个测试的 Servlet ,用来登录,和显示当前在线人数。编写退出操作,退出时候传入一个参数logout并重新访问servlet06,加入判断,如果识别到退出操作,就销毁session(OnlineServlet.java)

/**
 * 在线人数统计
 */
@WebServlet("/servlet06")
public class OnlineServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// 得到参数
		String key = request.getParameter("key");
		
		// 判断是否为空 (不为空,且值为logout则为退出操作)
		if (key != null && "logout".equals(key)) {
            // 传递了参数,表示要做用户退出操作
			request.getSession().invalidate();
            return;
        }
			
        // 创建session对象
        HttpSession session = request.getSession();
        // 获取sessio作用域中的在线人数
        Integer onlineNumber = (Integer) session.getServletContext().getAttribute("onlineNumber");						

        // 输出在线人数在页面
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("在线人数:"+onlineNumber+"</h2><h4><a href='servlet06?key=logout'>退出</a><h4>");	
	
	}
}

用不同的浏览器构建不同会话,访问serlvet06:

Servlet进阶开发_http_30


标签:session,浏览器,进阶,Servlet,开发,Cookie,import,servlet,javax
From: https://blog.51cto.com/u_15704423/6150623

相关文章