Servlet

Servlet

Servlet 是 Server 与 Applet 的缩写,是服务端⼩程序的意思。使⽤ Java 语⾔编写的服务器端程序, 可以像⽣成动态的 WEB ⻚,Servlet 主要运⾏在服务器端,并由服务器调⽤执⾏, 是⼀种按照 Servlet 标准来开发的类。 是 SUN 公司提供的⼀⻔⽤于开发动态 Web 资源的技术。(⾔外之意:要实现 web 开发,需要实现 Servlet 标准) 。

Servlet 本质上也是 Java 类,但要遵循 Servlet 规范进⾏编写,没有 main()⽅法,它的创建、使⽤、 销毁都由 Servlet 容器进⾏管理(如 Tomcat)。(⾔外之意:写⾃⼰的类,不⽤写 main ⽅法,别⼈⾃动 调⽤)。

Servlet 是和 HTTP 协议是紧密联系的,其可以处理 HTTP 协议相关的所有内容。这也是 Servlet 应⽤ ⼴泛的原因之⼀。 提供了 Servlet 功能的服务器,叫做 Servlet 容器,其常⻅容器有很多,如 Tomcat, Jetty, WebLogic Server, WebSphere, JBoss 等等。

创建Web项目

  1. 选择 “File” —> “New” —> “Project”

在这里插入图片描述

2.设置项目的相关信息,选择"Next"

在这里插入图片描述

  1. 设置项目名称及工作空间

在这里插入图片描述

  1. web项目目录结构如下:

在这里插入图片描述

Servlet的实现

新建类
  1. 点击"src" -->“ new”–>“package”–>,创建一个文件包

在这里插入图片描述

  1. 在包下面创建java类文件,点击包名–>“New”—>“java class”

在这里插入图片描述

  1. 创建一个普通的Java类

在这里插入图片描述

实现Servlet规范

实现Servlet规范,即继承HttpServlet类,并到导入相应的包,该类中已经完成了通信的规则,我们只需要进行业务的实现即可

import javax.servlet.http.HttpServlet;
public class Servlet01 extends HttpServlet {
}
重写service方法

满⾜ Servlet 规范只是让我们的类能够满⾜接收请求的要求,接收到请求后需要对请求进⾏分析,以 及进⾏业务逻辑处理,计算出结果,则需要添加代码,在规范中有⼀个叫做 service的⽅法,专⻔⽤来 做请求处理的操作,业务代码则可以写在该⽅法中。

package com.ysh.text;

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

public class TestServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("hello servlet");
        System.out.println("你好吗");
        System.out.println("hello servlet");
    }
}

设置注解

在完成好了⼀切代码的编写后,还需要向服务器说明,特定请求对应特定资源。

开发servlet项⽬,使⽤@WebServlet将⼀个继承于javax.servlet.http.HttpServlet 的类定义为Servlet 组件。在Servlet3.0中 , 可以使⽤@WebServlet注解将⼀个继承于javax.servlet.http.HttpServlet的类 标注为可以处理⽤户请求的 Servlet。

package com.ysh.text;

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;
import java.util.Arrays;

@WebServlet("/hel")
public class TestServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("hello servlet");
        System.out.println("你好吗");
        System.out.println("hello servlet");
        
    }
}

如果运行以上代码控制台输出出现乱码现象,以下为解决方案:

首先检查自己idea配置字节码格式

在这里插入图片描述

改为utf-8字节码格式

在这里插入图片描述

点击Edit Configurations

在这里插入图片描述

向VM options这一栏加入:-Dfile.encoding=UTF-8

在这里插入图片描述

点击Help一栏中的Edit Custom VM Options

在这里插入图片描述

加入-Dfile.encoding=UTF-8或者-Dfile.encoding=UTF8,修改后记得重启idea,这样控制台乱码以及Tomcat日志乱码就解决了

在这里插入图片描述

用注解配置Servlet

@WebServlet(name="Servlet01",value="/ser01")
@WebServlet(name="Servlet01",urlPatterns = "/ser01")

也可以配置多个访问路径

@WebServlet(name="Servlet01",value={"/ser01",'/ser001'})
@WebServlet(name="Servlet01",urlPatterns={"/ser01",'/ser001'})
发布项目并启动服务

到此,需要编写和配置的地⽅已经完成,项⽬已经完整了,但是如果需要外界能够访问, 还需要将项 ⽬发布到服务器上并运⾏服务器。

  1. 设置项目的站点名(项目对外访问路径)

在这里插入图片描述

在这里插入图片描述

  1. 设置项目的Tomcat配置

在这里插入图片描述

  1. 启动服务器

在这里插入图片描述

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 的⽣命周期。

init ⽅法,在 Servlet 实例创建之后执⾏(证明该 Servlet 有实例创建了)

public void init(ServletConfig config) throws ServletException {
 System.out.println("实例创建了...");
 }

service ⽅法,每次有请求到达某个 Servlet ⽅法时执⾏,⽤来处理请求(证明该Servlet 进⾏服务 了)

protected void service(HttpServletRequest req, HttpServletResponse resp)
 throws ServletException, IOException {
 System.out.println("服务调⽤了...");
 }

destroy ⽅法,Servlet 实例销毁时执⾏(证明该 Servlet 的实例被销毁了)

 public void destroy() {
 System.out.println("实例销毁了...");
 }
package com.ysh.text;

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;

/**
 * servlet的生命周期
 */
@WebServlet("/life")
public class ServletLife extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("index.jsp").forward(req,resp);
    }

    @Override
    public void destroy() {
        System.out.println("销毁servlet");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("初始化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 封装好传递过来。

HttpServletRequest 是 ServletRequest 的⼦接⼝,ServletRequest 只有⼀个⼦接⼝,就是 HttpServletRequest。既然只有⼀个⼦接⼝为什么不将两个接⼝合并为⼀个?

从⻓远上讲:现在主要⽤的协议是 HTTP 协议,但以后可能出现更多新的协议。若以后想要⽀持这种 新协议,只需要直接继承 ServletRequest 接⼝就⾏了。

在 HttpServletRequest 接⼝中,定义的⽅法很多,但都是围绕接收客户端参数的。但是怎么拿到该 对象呢?不需要,直接在 Service ⽅法中由容器传⼊过来,⽽我们需要做的就是取出对象中的数据,进 ⾏分析、处理。

接收请求

常用方法
getRequestURL()获取客户端发出请求时的完整URL
getRequestURI()获取请求行中的资源名称部分(项目名称开始)
getQueryString()获取请求行中的参数部分
getMethod()获取客户端请求方式
getProtocol()获取HTTP版本号
getContextPath()获取webapp名字,即(站点名)
获取请求参数
getParameter(String name)获取指定名称的参数
getParameterValues(String name)获取指定名称参数的所有值

实例

package com.ysh.text;

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;
import java.util.Arrays;

@WebServlet("/hel")
public class TestServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("hello servlet");
        System.out.println("你好吗");
        System.out.println("hello servlet");
        //req.getRequestDispatcher("ser02").forward(req,resp);

        //获取客户端请求的完整url,从http开始,到?前面结束
        String url = request.getRequestURL().toString();
        System.out.println(url);//http://localhost:8080/11_16Servlet/hel

        //获取客户端请求的部分uri(从站点名开始,到?前面结束)
        String uri = request.getRequestURI().toString();
        System.out.println(uri); // /11_16Servlet/hel

        //获取请求行中的参数部分
        System.out.println(new String(request.getQueryString().getBytes("iso-8859-1"), "utf-8"));
        //System.out.println(new String(bytes,0,bytes.length,"utf-8"));  //uname=yang&age=19

        //获取客户端的请求方式
        String method = request.getMethod();
        System.out.println(method);  //GET

        //获取http的版本号
        String protocol = request.getProtocol();
        System.out.println(protocol);  // HTTP/1.1

        //获取webapp名字(站点名)
        String contextPath = request.getContextPath();
        System.out.println(contextPath);  // /11_16Servlet

        //获取指定名称的参数,返回字符串
        String uname = request.getParameter("uname");
        System.out.println(uname);

        //如果一个表单中是一个复选框,提交表单时有多个值,这时候可以使用getParameterValues方法
        String[] hoppies = request.getParameterValues("hoppy");
        System.out.println(Arrays.toString(hoppies));
    }
}

请求乱码问题

由于现在的 request 属于接收客户端的参数,所以必然有其默认的语⾔编码,主要是由于在解析过程 中默认使⽤的编码⽅式为 ISO-8859-1(此编码不⽀持中⽂),所以解析时⼀定会出现乱码。要想解决这种 乱码问题,需要设置 request 中的编码⽅式,告诉服务器以何种⽅式来解析数据。或者在接收到乱码数 据以后,再通过相应的编码格式还原。

方式一: 只针对 POST 有效(必须在接收所有的数据之前设定)

request.setCharacterEncoding("UTF-8");

方式二:

new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");

借助了String 对象的⽅法,该种⽅式对任何请求有效,是通⽤的。

但是从Tomcat8起,以后的GET⽅式请求是不会出现乱码的

如果你使用的时Tomcat8以前的,如果使用的该方法仍然出现乱码现象,请参考以上乱码解决措施

请求转发

请求转发,是⼀种服务器的⾏为,当客户端请求到达后,服务器进⾏转发,此时会将请求对象进⾏保 存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始⾄终只有⼀ 个请求发出。

实现⽅式如下,达到多个资源协同响应的效果。

request.getRequestDispatcher(url).forward(request,response);

request作用域

通过该对象可以在⼀个请求中传递数据,作⽤范围:在⼀次请求中有效,即服务器跳转有效。

// 设置域对象内容
request.setAttribute(String name, String value);
// 获取域对象内容
request.getAttribute(String name);
// 删除域对象内容
request.removeAttribute(String name);
package com.ysh.text;

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;
@WebServlet("/pt")
public class ServletPostTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      //由于这里我是post请求拿到的数据,如果不设置字符编码格式,则传过来的如果是中文则会出现乱码
       request.setCharacterEncoding("utf-8");  //规定字符编码格式
       String name=request.getParameter("name");
       System.out.println(name);
       if (name.equals("admin")){
           //使用getRequestDispatcher方法地址栏不会发生改变,但是页面的内容会发生改变
           request.getRequestDispatcher("/index.jsp").forward(request,response);
       }else {
           request.setAttribute("msg","用户名错误");
           request.getRequestDispatcher("post_login.jsp").forward(request,response);
       }
    }
}

HttpServletResponse对象

Web服务器收到客户端的http请求,会针对每⼀次请求,分别创建⼀个⽤于代表请求的 request 对象 和代表响应的 response 对象。

request 和 response 对象代表请求和响应:获取客户端数据,需要通过 request 对象;向客户端输 出数据,需要通过 response 对象。

HttpServletResponse 的主要功能⽤于服务器对客户端的请求进⾏响应,将 Web 服务器处理后的结 果返回给客户端。service()⽅法中形参接收的是 HttpServletResponse 接⼝的实例化对象,这个对象中 封装了向客户端发送数据、发送响应头,发送响应状态码的⽅法。

响应数据

接收到客户端请求后,可以通过 HttpServletResponse 对象直接进⾏响应,响应时需要获取输出流。 有两种形式:

  • getWriter() 获取字符流(只能响应回字符)
  • getOutputStream() 获取字节流(能响应⼀切数据)

响应回的数据到客户端被浏览器解析。

注意:两者不能同时使⽤。

package com.ysh.text;

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;
@WebServlet("/pt")
public class ServletPostTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      //由于这里我是post请求拿到的数据,如果不设置字符编码格式,则传过来的如果是中文则会出现乱码
       request.setCharacterEncoding("utf-8");  //规定字符编码格式
       String name=request.getParameter("name");
       System.out.println(name);
       if (name.equals("admin")){
           //使用getRequestDispatcher方法地址栏不会发生改变,但是页面的内容会发生改变
           request.getRequestDispatcher("/index.jsp").forward(request,response);
       }else {
           request.setAttribute("msg","用户名错误");
           request.getRequestDispatcher("post_login.jsp").forward(request,response);
       }
    }
}

jsp代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>post</title>
</head>
<body>
<form method="post" action="pt">
    姓名:<input type="text" name="name"/><%=request.getAttribute("msg") %>
    <input type="checkbox" name="hobby" value="basketball"/>篮球
    <input type="checkbox" name="hobby" value="pingpang"/>乒乓球
    <input type="submit"/>
</form>
</body>
</html>

响应乱码问题

在响应中,如果我们响应的内容中含有中⽂,则有可能出现乱码。这是因为服务器响应的数据也会经 过⽹络传输,服务器端有⼀种编码⽅式,在客户端也存在⼀种编码⽅式,当两端使⽤的编码⽅式不同时 则出现乱码。

getWriter()的字符乱码

对于 getWriter()获取到的字符流,响应中⽂必定出乱码,由于服务器端在进⾏编码时默认会使⽤ ISO-8859-1 格式的编码,该编码⽅式并不⽀持中⽂。

要解决该种乱码只能在服务器端告知服务器使⽤⼀种能够⽀持中⽂的编码格式,⽐如我们通常⽤ 的"UTF-8"。

response.setCharacterEncoding("UTF-8");

此时还只完成了⼀半的⼯作,要保证数据正确显示,还需要指定客户端的解码⽅式。

response.setHeader("content-type", "text/html;charset=UTF-8");

两端指定编码后,乱码就解决了。⼀句话:保证发送端和接收端的编码⼀致

getOutputStream()字节乱码

对于 getOutputStream()⽅式获取到的字节流,响应中⽂时,由于本身就是传输的字节, 所以此时可 能出现乱码,也可能正确显示。当服务器端给的字节恰好和客户端使⽤的编码⽅式⼀致时则⽂本正确显 示,否则出现乱码。⽆论如何我们都应该准确掌握服务器和客户端使⽤的是那种编码格式,以确保数据 正确显示。

首先也是要指定客户端和服务器使⽤的编码⽅式⼀致。

客户端设置可以使用以下两种方式的一种:

response.setHeader("content-type","text/html;charset=UTF-8");
response.setContentType("text/html;charset=UTF-8");

服务器端设置

// 设置服务端的编码
response.setCharacterEncoding("UTF-8");

总结:要想解决响应的乱码,只需要保证使⽤⽀持中⽂的编码格式。并且保证服务器端 和客户端使⽤相 同的编码⽅式即可

代码实例:

package com.ysh.text;

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

/**
 * 客户端发送数据到服务器端,此时服务器要作出响应,响应时需要获取输出流(输出字符流,输出字节流)
 */
@WebServlet("/resp")
public class ServletResponse extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解决响应乱码问题
        response.setCharacterEncoding("utf-8");
        //使页面能够识别html标签
        //第一种方式
        //response.setHeader("content-type","text/html;charset=UTF-8");
        //第二种方式
        response.setContentType("text/html;charset=UTF-8");

        /*PrintWriter writer = response.getWriter();
        //字符输出流
        writer.write("hello");
        writer.write("<h1>你也好</h1>");
        //刷出
        writer.flush();
        //关流
        writer.close();*/

        //字节流
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write("hello".getBytes());
        outputStream.write("<h1>你也好</h1>".getBytes());
        outputStream.flush();
        outputStream.close();
    }
}

重定向

重定向是⼀种服务器指导,客户端的⾏为。客户端发出第⼀个请求,被服务器接收处理后,服务器会 进⾏响应,在响应的同时,服务器会给客户端⼀个新的地址(下次请求的地址 response.sendRedirect(url);),当客户端接收到响应后,会⽴刻、⻢上、⾃动根据服务器给的新地址 发起第⼆个请求,服务器接收请求并作出响应,重定向完成。

从描述中可以看出重定向当中有两个请求存在,并且属于客户端⾏为。

// 重定向跳转到index.jsp
response.sendRedirect("index.jsp");

通过观察浏览器我们发现第⼀次请求获得的响应码为 302,并且含有⼀个 location 头信息。并且地址 栏最终看到的地址是和第⼀次请求地址不同的,地址栏已经发⽣了变化。

在这里插入图片描述

请求转发与重定向的区别

请求转发(request.getRequestDispatcher.forward())重定向(request.sendRedirect())
一次请求,数据在request域中共享两次请求,request域中数据不共享
服务器行为客户端行为
地址栏不发生变化地址栏发生变化
绝对地址定位到站点后绝对地址可写到http://

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("uname","zhangsan");
// 发送Cookie对象
response.addCookie(cookie);

Cookie的获取

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

package com.ysh;

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

@WebServlet("/ser02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie[] cookies = request.getCookies();
        if(cookies!=null && cookies.length>0){
            for (Cookie cookie:cookies){
                String name = cookie.getName();
                String value = cookie.getValue();
                System.out.println(name+"--"+value);
            }
        }
    }
}

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。

    package com.ysh;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/ser03")
    public class Servlet03_Time extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //设置cookie到期时间
            Cookie cookie1 = new Cookie("hahha", "yang");
            //默认关闭浏览器,则cookie消失
            //cookie1.setMaxAge(-1);
            //存储的秒数
            //cookie1.setMaxAge(10);
            //删除cookie
            cookie1.setMaxAge(0);
            //服务端向浏览器作出回应
            response.addCookie(cookie1);
        }
    }
    

    Cookie的注意点

    1. Cookie保存在当前浏览器中。

      在⼀般的站点中常常有记住⽤户名这样⼀个操作,该操作只是将信息保存在本机上,换电脑以后这 些信息就⽆效了。⽽且 cookie 还不能跨浏览器。

    2. Cookie存中⽂问题

      Cookie 中不能出现中⽂,如果有中⽂则通过 URLEncoder.encode()来进⾏编码,获取时通过 URLDecoder.decode()来进⾏解码。

    3. 同名Cookie问题

      如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。

    4. 浏览器存放Cookie的数量

      不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器) 的,⽽且⼀般是由服务器端创建和设定。后期结合Session来实现回话跟踪。

注意:之前HttpServletRequest中的getQueryString方法:如果在地址栏输出中文,然后利用getQueryString()方法获取参数名以及参数值,如果参数值是中文,那么这时候可以利用URLDecoder.decode()来对其进行解码然后输出即解决了中文无法在控制台输出的现象了。

package com.ysh;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;

@WebServlet("/ser04")
public class Servlet04NameCode extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name="张三";
        String gender="男";
        name= URLEncoder.encode(name);
        gender= URLEncoder.encode(gender);
        Cookie cookie1 = new Cookie(name,gender);
        response.addCookie(cookie1);

    }
}

Cookie的路径

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

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

/* 当前项⽬路径为:s01 */
Cookie cookie = new Cookie("name","喜羊羊");
// 设置路径为"/",表示在当前服务器下任何项⽬都可访问到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);

以下项目是在s01项目路径下的,首先运行以下服务器,然后运行s02项目然后运行ser02,以下的cookie就会显示出来

package com.ysh;

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

@WebServlet("/ser06")
public class ServletPath06 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie cookie = new Cookie("yang", "hhhh");
        cookie.setPath("/s02/ser02");
        response.addCookie(cookie);
    }
}

如果我们设置path,如果当前访问的路径包含了cookie的路径(当前访问路径在cookie路径基础上要 ⽐cookie的范围⼩)cookie就会加载到request对象之中。

cookie的路径指的是可以访问该cookie的顶层⽬录,该路径的⼦路径也可以访问该cookie。

总结:当访问的路径包含了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 来实现。

session域对象

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

// 获取session对象
HttpSession session = request.getSession();
// 设置session域对象
session.setAttribute("uname","admin");
// 获取指定名称的session域对象
String uname = (String) request.getAttribute("uname");
// 移除指定名称的session域对象
session.removeAttribute("uname");

数据存储在 session 域对象中,当 session 对象不存在了,或者是两个不同的 session 对象时,数据 也就不能共享了。这就不得不谈到 session 的⽣命周期。

session对象的销毁

默认时间到期

当客户端第⼀次请求 servlet 并且操作 session 时,session 对象⽣成,Tomcat 中 session 默认的存 活时间为 30min,即你不操作界⾯的时间,⼀旦有操作,session 会重新计时。

那么 session 的默认时间可以改么?答案是肯定的。

可以在 Tomcat 中的 conf ⽬录下的 web.xml ⽂件中进⾏修改。

<!-- session 默认的最⼤不活动时间。单位:分钟。 -->
<session-config>
 <session-timeout>30</session-timeout>
</session-config>
自己设定到期时间

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

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

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

// 获取session的最⼤不活动时间
int time = session.getMaxInactiveInterval();
package com.ysh;

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;

@WebServlet("/stt01")
public class Session_TimeTest01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //设置2秒后session自动销毁,销毁后尽管在浏览器中刷新,但是在控制台后得到域中的age的值为null
        //由于session会话是依赖cookie,如果不关闭浏览器窗口,相当于session一直保留
        session.setMaxInactiveInterval(2);
        session.setAttribute("age",19);
        response.sendRedirect("stt02");
    }
}

package com.ysh;

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;

@WebServlet("/stt02")
public class Session_TimeTest02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            HttpSession session = request.getSession();
            System.out.println(session.getAttribute("age"));
    }
}

在这里插入图片描述

立刻失效

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

// 销毁session对象
session.invalidate();
关闭浏览器

从前⾯的 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. 直接获取,Servlet类中提供了直接获取ServletContext对象的方法
ServletContext servletContext = getServletContext();

常用方法:

// 获取项⽬存放的真实路径
String realPath = request.getServletContext().getRealPath("/");
// 获取当前服务器的版本信息
String serverInfo = request.getServletContext().getServerInfo();

ServletContext域对象

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

package com.ysh;

import javax.servlet.ServletContext;
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;

@WebServlet("/sc01")
public class ServerContextTest01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        servletContext.setAttribute("name","hahahahahh");
        //response.sendRedirect("sc02");
    }
}

package com.ysh;

import javax.servlet.ServletContext;
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;

@WebServlet("/sc02")
public class ServerContextTest02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        System.out.println(servletContext.getAttribute("name"));
        //response.sendRedirect("sc02");
    }
}

在这里插入图片描述

Servlet的三大域对象

  1. request域对象

    在⼀次请求中有效。请求转发有效,重定向失效。

  2. session域对象

    在⼀次会话中有效。请求转发和重定向都有效,session销毁后失效。

  3. servletContext域对象

    在整个应⽤程序中有效。服务器关闭后失效。

文件上传和下载

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

文件上传

⽂件上传涉及到前台⻚⾯的编写和后台服务器端代码的编写,前台发送⽂件,后台接收并保存⽂件, 这才是⼀个完整的⽂件上传。

前台页面

在做⽂件上传的时候,会有⼀个上传⽂件的界⾯,⾸先我们需要⼀个表单,并且表单的请求⽅式为 POST;其次我们的 form 表单的 enctype 必须设为"multipart/form-data",即 enctype=“multipart/form-data”,意思是设置表单的类型为⽂件上传表单。默认情况下这个表单类 型是 “application/x-www-form-urlencoded”, 不能⽤于⽂件上传。只有使⽤了multipart/form-data 才 能完整地传递⽂件数据。


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
<!--文件上传一定是post请求-->
<form action="up01" method="post" enctype="multipart/form-data">
    <input type="file" name="myfile"/>文件上传
    <input type="submit" value="提交"/>
</form>
</body>
</html>

后台实现

使⽤注解 @MultipartConfig 将⼀个 Servlet 标识为⽀持⽂件上传。 Servlet 将 multipart/formdata 的 POST 请求封装成 Part,通过 Part 对上传的⽂件进⾏操作。

package com.ysh.review01UpLoad;

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;

@WebServlet("/up01")
@MultipartConfig
public class UpLoadTest01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先接收前台表单中传过来的文件信息,即表单中name属性值
        Part part = request.getPart("myfile");
        //获取从表单中上传文件的文件名以及后缀
        String submittedFileName = part.getSubmittedFileName();
        System.out.println(submittedFileName);
        //确认上传之后存放的路径
        String realPath = request.getServletContext().getRealPath("/");
        //上传之后文件存放的位置以及文件名
        part.write(realPath+submittedFileName);
    }
}

文件下载

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

超链接下载

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

默认下载

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

指定download属性下载

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>
<a href="01.png" download>图片1</a>
<!--下载该图片,并且命名为abc,文件的类型即后缀名保留-->
<a href="02.png" download="abc">图片2</a>
<a href="Test.txt">文件1</a>
<a href="FeiQ.exe">文件2</a>
</body>
</html>
后台实现下载

实现步骤

  1. 需要通过 response.setContentType ⽅法设置 Content-type 头字段的值, 为浏览器⽆法使⽤某 种⽅式或激活某个程序来处理的 MIME 类型,例 如 “application/octet-stream” 或 “application/x-msdownload” 等。
  2. 需要通过 response.setHeader ⽅法设置 Content-Disposition 头的值 为 “attachment;filename= ⽂件名” 。
  3. 读取下载⽂件,调⽤ response.getOutputStream ⽅法向客户端写⼊附件内容

前端代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件下载</title>
</head>
<body>
<form method="post" action="dl">
    文件名:<input type="text" name="myfile"> <br>
    <button type="submit">提交</button>
</form>
</body>
</html>

package com.ysh.review02DownLoad;

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;

@WebServlet("/dl")
public class DownLoadTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先规定编码格式
        request.setCharacterEncoding("utf-8");
        //要下载哪个文件,首先要获取下载路劲的地址
        String realPath = getServletContext().getRealPath("/");
        //从前端表单中获取下载的文件名,这里的文件名包含后缀
        String filename = request.getParameter("myfile");
        System.out.println(filename);
        String path=realPath+filename;
        File file=new File(path);
        //然后判断该路径下该文件是否真正存在,以及是否是一个文件
        if (file.exists()&&file.isFile()){
            //是一个文件则把结果集响应给前台
            //设定响应类型
            response.setContentType("application/x-msdownload");
            //设置头信息
            //"Content-Disposition", "attachment;filename=" + "hello"
            //"Content-Disposition", "attachment;filename=" +
            response.setHeader("Content-Disposition","attachment;filename=" +filename);
            //获取输出流
            FileInputStream fis = new FileInputStream(file);
            //获取输入流
            ServletOutputStream outputStream = response.getOutputStream();
            //采用边读边写
            byte[] bytes=new byte[1024];
            int len;
            while ((len=fis.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
            }
            //关闭资源
            outputStream.close();
            fis.close();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值