servlet笔记

116 阅读26分钟

概述

image.png

HTTP协议

超文本传输协议,是客户端请求和响应的标准协议,数据传送和解析时都遵守的规则

  • 支持客户/服务器模式
  • 简单灵活快速:支持传输任意类型的数据对象,客户向服务端传输请求时,只传送请求方法和路径。
  • 无连接:每次连接只传送一个请求(带饭,带饮料,拿快递),HTTP1.1后支持可持续连接(带饭且带饮料且拿快递)
  • 无状态:没有记忆能力,需要先前信息时必须重传(带饭,带鱼香肉丝饭)

image.png

image.png

请求协议

客户端发送数据给服务器的格式,由三部分组成:请求行、请求头、请求体

格式:

请求行:请求方法(GET/POST)+URI统一资源标识符+HTTP版本+回车换行
请求头1:名字+“:”+空格+值
请求头2:名字+“:”+空格+值
...
请求空行
请求体(GET无请求体)

特别的,请求头Referer:指明请求从哪里来,即跳转该连接前的网址,应用在防盗链、百度广告等

image.png

响应协议

服务器发送数据给客户端的格式,由三部分组成:响应行(状态行)、响应头、响应正文

响应行:HTTP版本+状态码(200、404等)+状态解析(不同浏览器说法可能不同)
响应头1:名字+“:”+空格+值
响应头2:名字+“:”+空格+值
...
响应空行
响应体

特别的,响应头Location:重定向接收者到一个新的位置,常用于更换域名时
响应头Refresh:自动跳转(单位是秒),可以在页面同构mate标签实现,也可在后台实现

image.png

Tomcat服务器

tomcat是一个运行Java的网络服务器,底层是socket程序,安装完成后启动时点击bin目录下的startup命令然后点击在浏览器输入localhost:8080127.0.0.1:8080;关闭时点击bin目录下的shutdown命令。
目录结构: image.png

idea

添加多行注释CTRL+shift+/;添加文档注释/**+enter
教程中使用的是idea2019,而我使用的是idea2024,因此有许多设置是不同的,网上对于新版idea如何配置jsp的教程也是五花八门,试了很多次都没有成功,几近崩溃。终于在卡了一天后摸索出了配置方法,虽然不懂什么原理,但好歹能用了,为防止忘记在此记录。
如果项目文件夹是第一次构建web项目,需要的步骤有些复杂,如果文件夹中已经存在web项目,则只需要操作第一步即可

  1. 新建项目,选择javaee项目,设置如图
    屏幕截图 2024-05-31 183447.png 屏幕截图 2024-05-31 183457.png
  2. 打开项目后双击shift,搜索添加框架支持,选中web应用程序,版本默认4.0 屏幕截图 2024-05-31 183716.png
  3. 重启软件,此时右下角会弹出检测到默认maven配置和web框架,都点配置,idea就会帮我们配好环境,不需要自己配maven和tomocat之类的 屏幕截图 2024-05-31 183824.png
  4. 完成配置后页面如图,可以点右上角运行或调试了,如果卡住了就关掉重启一下
    屏幕截图 2024-05-31 183916.png

另外,可以在部署里更改应用访问路径
屏幕截图 2024-05-31 211116.png

servlet实现

  1. 创建软件包命名为com.xxx.servlet
  2. 创建普通java类
  3. 实现servlet规范,继承HttpServlet类,该类中已经完成了通信的规则
  4. 重写service方法,用来处理请求
  5. 设置注解,指定访问的路径,也就是站点名使用@WebServlet
package com.example.servlet01;

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("/ser01")
//@WebServlet(name = "Servlet01", value = "/ser01")
//@WebServlet(name = "Servlet01", value = {"/ser01", "/ser001"})
//@WebServlet(name = "Servlet01", urlPatterns = "/ser01")
@WebServlet(name = "Servlet01", urlPatterns = {"/ser01", "/ser001"})
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //打印内容
        System.out.println("Hello Servlet");
        //通过流输出数据到浏览器
        resp.getWriter().write("Hello Servlet");
    }
}

流程

浏览器中输入地址后

  1. host找到主机
  2. 请求行找到是哪个web应用程序和具体的路径,找到是哪个资源
  3. 调用service方法,生成req、resp对象,来处理请求和响应
  4. 把输出返回出去 屏幕截图 2024-05-31 212526.png

servlet生命周期

image.png

servlet没有main()方法,不能独立运行,它的运行完全由servlet引擎来控制和调度,因此生命周期指的是servlet容器何时创建、调用方法进行请求的处理、销毁实例的整个过程

  • 实例和初始化时机:请求到达容器时就会查找servlet容器是否存在,若不存在则创建实例并进行初始化
  • 就绪/调用/服务阶段:调用servlet的service()方法,在整个生命周期中可以被多次调用
  • 销毁时机:容器关闭(应用程序停止时),会将容器的servlet实例进行销毁

通过创建、服务、销毁的三个方法来观察一下servlet的生命周期吧

package com.example.servlet01;

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("/ser02")
public class Servlet02 extends HttpServlet {

    /**
     * 就绪/服务方法(请求处理数据)
     * 系统方法,服务器自动调用
     * 当有请求到达servlet时,就会自动调用该方法
     * 方法可以被多次调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servlet被调用了");
    }

    /**
     *初始化方法
     * 当请求到达servlet容器时,servlet容器就会判断该servlet对象是否存在,若不存在,则创建实例并初始化
     * 方法只会执行一次
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        System.out.println("servlet被创建了");
    }

    /**
     *销毁方法
     * 系统方法,服务器自动调用
     * 当服务器关闭或应用程序停止时,就自动调用该方法
     * 方法只会调用一次
     */
    @Override
    public void destroy() {
        System.out.println("servlet被销毁了");
    }
}

屏幕截图 2024-06-01 140829.png image.png

request对象

定义

HttpServletRequest对象用来接收客户端发来的请求信息,其中有许多方法。
它是由tomcat封装好传递过来的,service方法中的形参接受的就是HttpServletRequest接口的实例化对象

常用方法

  • getRequestURL():获取请求时的完整路径
  • getRequestURI():获取请求时的部分路径
  • getQueryString():获取请求时的参数字符串
  • getMethod():获取客户端的请求方式
  • getProtocol():获取当前协议版本
  • getContextPath():获取项目的站点名
  • getParameter("参数名"):获取指定名称的参数值
  • getParameterValues("参数名"):获取指定名称的参数的所有参数值
package com.example.sevelt02request;

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("/ser01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*常用方法*/
        //获取请求时的完整路径(从http开始,到"?"前面结束)
        String url = req.getRequestURL() + "";
        System.out.println("取请求时的完整路径:" + url);

        //获取请求时的部分路径(从项目的站点名开始,到"?"前面结束)
        String uri = req.getRequestURI();
        System.out.println("取请求时的部分路径:" + uri);

        //获取请求时的参数字符串(从"?"后面开始,到最后的字符串)
        String query = req.getQueryString();
        System.out.println("获取请求时的参数字符串:" + query);

        //获取客户端的请求方式(GET和POST)
        String method = req.getMethod();
        System.out.println("获取客户端的请求方式:" + method);

        //获取当前协议版本(HTTP/1.1)
        String protocol = req.getProtocol();
        System.out.println("获取当前协议版本:" + protocol);

        //获取项目的站点名(项目对外访问路径)
        String contextPath = req.getContextPath();//上下文路径
        System.out.println("获取项目的站点名:" + contextPath);

        /*获取请求的参数*/
        //获取指定名称的参数值,返回字符串(常用!重点!!)
        String uname = req.getParameter("uname");
        String upwd = req.getParameter("upwd");
        System.out.println("uname:" + uname + ",upwd:" + upwd);

        //获取指定名称的参数的所有参数值,返回字符串数组(用于复选框传值)
        String[] hobbys = req.getParameterValues("hobby");
        //判断数组是否为空
        if (hobbys != null && hobbys.length > 0) {
            for (String hobby : hobbys) {
                System.out.println("爱好:" + hobby);
            }
        }
    }
}

屏幕截图 2024-06-01 162616.png

乱码

由于字符编码标准不同,中文可能会出现乱码,在tomcat8以上版本中,get请求不会乱码,但是post请求会乱码,对此的解决方法是在获取参数前设置编码格式

package com.example.sevelt02request;

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;

/**
 * 请求乱码问题
 * tomcat8以上版本:
 *      GET请求没有乱码问题
 *      POST请求会出现乱码,解决方法是在获取参数前设置请求的编码格式req.setCharacterEncoding("utf-8");
 * tomcat7以下版本:
 *      GET请求也会出现乱码问题,解决方法是在获取参数后转换字符格式String name=new String(req.getParameter(uname).getBytes("iso-8859-1"),"utf-8");
 *      POST方法也会出现乱码问题,以上两种解决方法均可
 */
@WebServlet("/s02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求的编码格式
        req.setCharacterEncoding("utf-8");//只针对post请求乱码有效
        //获取客户端传递的参数
        String uname = req.getParameter("uname");
        String upwd = req.getParameter("upwd");
        System.out.println("姓名:" + uname);
        System.out.println("密码:"+upwd);
        //String name=new String(req.getParameter(uname).getBytes("iso-8859-1"),"utf-8");//对GET和POST均有效
    }
}

请求转发

服务器收到客户端请求后,再跳转回客户端
是一种服务器行为,服务器进行转发时会将请求对象保存到本地,地址栏中的url不会改变自始至终只有一个请求发出

package com.example.sevelt02request;

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)
 * req.getRequestDispatcher(url).forward(req, resp);
 *
 * 特点:
 *  1. 服务器行为
 *  2. 地址栏不发生改变
 *  3. 从始至终只有一个请求
 *  4. request数据可以共享
 */
@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //接收客户端请求的参数
        String uname = req.getParameter("uname");
        System.out.println("Servlet03 uname:"+uname);

        //请求转发跳转到Servlet04
        //req.getRequestDispatcher("s04").forward(req, resp);
        //请求转发跳转到jsp页面
        //req.getRequestDispatcher("login.jsp").forward(req, resp);
        //请求转发跳转到html页面
        //req.getRequestDispatcher("login.html").forward(req, resp);
    }
}
 
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uname = req.getParameter("uname");
        System.out.println("Servlet04 uname:" + uname);
    }
}

image.png

Request域对象

也称为作用域,这个对象的作用域是一次请求中,因此转发跳转时有效,也可以多次跳转。
用途是在服务器和客户端或服务器之间传递对象,类似于全局变量吧

package com.example.sevelt02request;

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

/**
 * request域对象
 * 通过该对象,可以在一个请求中传递数据,作用范围是一次请求中,即请求转发跳转时有效
 * //设置域对象内容
 * req.setAttribute(String name, Object value);
 * //获取域对象内容
 * req.getAttribute(String name);
 * //删除域对象内容
 *
 */
@WebServlet("/s05")
public class Servlet05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet05...");

        //设置域对象内容
        req.setAttribute("name", "admin");
        req.setAttribute("age", 22);
        List<String> list = new ArrayList<String>();
        list.add("aaa");
        list.add("bbb");
        req.setAttribute("list", list);

        //请求转发跳转到Servlet06
        req.getRequestDispatcher("s06").forward(req, resp);
        //请求转发到jsp并通过与对象传递数据
        //req.getRequestDispatcher("test.jsp").forward(req, resp);
    }
}

@WebServlet("/s06")
public class Servlet06 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet06...");

        //获取域对象内容
        String name= (String) req.getAttribute("name");
        System.out.println("name:"+name);
        Integer age= (Integer) req.getAttribute("age");
        System.out.println("age:"+age);
        List<String> list= (List<String>) req.getAttribute("list");
        System.out.println(list.get(0));
    }
}

<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>index页面</h2>
<%--如果要在jsp中写Java代码,需要写在脚本段中--%>
<%--jsp中不能写req,要写request--%>
<%
    //获取域对象内容
    String name= (String) request.getAttribute("name");
    System.out.println("name:"+name);
    Integer age= (Integer) request.getAttribute("age");
    System.out.println("age:"+age);
    List<String> list= (List<String>) request.getAttribute("list");
    System.out.println(list.get(0));
%>
</body>
</html>

屏幕截图 2024-06-04 154933.png

Respons对象

request对象代表请求;response对象代表响应,向客户端输出数据需要通过response对象。
receive方法中形参接收的是HttpServletResponse接口的实例化对象,这个对象中封装了向客户端发送响应数据、响应头、响应状态码的方法。

响应数据

接收到客户端请求后,可通过HttpServletResponse对象直接进行响应,响应时需要获取输出流,有两种形式,但不可同时使用。响应回的数据到客户端被浏览器解析

package com.example.servlet03respons;

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;

/**
 * 响应数据
 * resp.getWriter()        字符输出流,只能输出字符串
 * resp.getOutputStream()  字节输出流,能输出一切数据
 * 二者使用后需关闭 out.close();,且二者不能同时使用,会报错
 */
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //getWriter()字符输出流,仅输出字符
        //获取字符输出流
        PrintWriter out = resp.getWriter();
        //输出数据
        out.write("Hello");
        //关闭输出流
        out.close();

        //getOutputStream() 字节输出流,输出一切数据
        //获取字节输出流
        //ServletOutputStream out = resp.getOutputStream();
        //输出数据,在括号里填编码格式,可不填
        //out.write("Hi".getBytes("UTF-8"));
        //关闭输出流
        //out.close();
    }
}

image.png

乱码

如果响应中含有中文,则可能出现乱码问题

getWriter()字符输出流

必定出现乱码因为服务器端进行编码时默认使用ISO-8859-1格式的编码,不支持中文
解决方法是告知服务器和客户端使用支持中文的编码格式且保持一致

package com.example.servlet03respons;

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;

/**
 * resp.getWriter()字符输出流乱码
 * 设置客户端和服务端的编码都支持中文且保持一致
 * 1. 设置服务端编码格式 resp.setCharacterEncoding("UTF-8");
 * 2. 设置客户端编码格式 resp.setHeader("Content-Type", "text/html;charset=UTF-8");
 * 也可以同时设置客户端服务端编码格式 resp.setContentType("text/html;charset=UTF-8");
 */
@WebServlet("/s02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置服务端编码格式
        resp.setCharacterEncoding("UTF-8");
        //设置客户端编码格式和响应的MIME类型
        resp.setHeader("Content-Type", "text/html;charset=UTF-8");

        //同时设置客户端和服务端的编码格式,常用
        resp.setContentType("text/html;charset=UTF-8");

        //获取并输出字符流
        PrintWriter out = resp.getWriter();
        out.write("<h2>你好</h2>");

    }
}

屏幕截图 2024-06-09 131116.png

getOutputStream()字节输出流

响应中文时,可能出现乱码也可能正常显示,我们要确保不出现乱码
处理方法和字符流相同

package com.example.servlet03respons;

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;
import java.nio.charset.StandardCharsets;

/**
 * resp.getOutputStream()字节输出流乱码
 * 与字符流乱码处理方式相同,常用同时设置
 */
@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //同时设置客户端和服务端的编码格式
        resp.setContentType("text/html;charset=UTF-8");

        //获取并输出字节输出流
        ServletOutputStream out = resp.getOutputStream();
        out.write("<h2>你好</h2>".getBytes("UTF-8"));

    }
}

image.png

重定向

是一种客户端行为,存在两次请求,地址栏会发生变化,数据不共享。
客户端发出第一个请求,服务器响应(响应码302)时给客户端一个新的地址,客户端收到响应后,会马上根据服务器给的新地址发起第二个请求,服务器接收请求并作出响应,重定向完成。

package com.example.servlet03respons;

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;

/**
 * 重定向
 * 服务器指导,客户端行为
 * 存在两次请求
 * 地址栏会发生改变
 * request对象不共享
 */
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet04...");

        //接收参数
        String uname = req.getParameter("uname");
        System.out.println("Servlet04 uname: " + uname);

        //重定向跳转到s05
        resp.sendRedirect("s05");
    }
}

image.png

image.png

请求转发与重定向的区别

image.png

package com.example.servlet03respons;

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;

/**
 * 请求转发与重定向的区别
 * 1. 请求转发的地址栏不会发生改变,重定向会发生改变
 * 2. 请求转发只有一次请求,重定向有两次
 * 3. 请求转发request对象可共享,重定向request对象不共享
 * 4. 请求转发是服务端行为,重定向是客户端行为
 * 5. 请求转发时的地址只能是当前站点(当前项目)下的,重定向的地址可以是任何地址
 * 因此,若想要跳转的地址不是当前项目下的,只能使用重定向
 */
@WebServlet("/s06")
public class Servlet06 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet06...");

        //接收参数
        String uname = req.getParameter("uname");
        System.out.println(uname);

        //设置request域对象
        req.setAttribute("upwd", "123456");

        //请求转发
        //req.getRequestDispatcher("hello.jsp").forward(req, resp);
        //可以接收并打印出参数和域对象

        //重定向
        //resp.sendRedirect("hello.jsp");
        //无法接收参数和域对象,只能打印出null

        //重定向到百度
        //resp.sendRedirect("https://www.baidu.com/");
    }
}


以下是hello.jsp的代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求转发与重定向</title>
</head>
<body>
<%--java脚本段--%>
<%
    //获取参数
    String uname=request.getParameter("uname");
    //获取request域对象
    String upwd= (String) request.getAttribute("upwd");

    //输出内容
    System.out.println(uname);
    System.out.println("----------");
    System.out.println(upwd);

%>
</body>
</html>

Cookie对象

http是无状态协议,当传输的数据很多时,服务器就需要链接很多次,cookie是服务器将一些只需要在客户端处理的数据保存在客户端,无需通过网页传输,以提高网页效率减少服务器负载。但是安全性较差。例如记住密码。
Cookie的格式:键值用=链接,多个键值用;隔开

cookie的创建和发送和获取

package com.example.servlet04cookie;

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;

/**
 * Cookie的创建和发送(响应)和获取
 * 创建:new一个cookie对象
 * 发送:resp.addCookie
 * 获取:req.getCookies
 * 获取cookie是返回一个cookie数组
 */

@WebServlet("/c01")
public class Cookie01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //Cookie的创建
        Cookie cookie = new Cookie("name", "admin");
        //Cookie的发送
        resp.addCookie(cookie);

        //获取Cookie数组
        Cookie[] cookies = req.getCookies();
        //判断Cookie是否为空
        if (cookies != null && cookies.length > 0) {
            //遍历Cookie数组
            for (Cookie c : cookies) {
                //获取并打印cookie的名称和值
                String name = c.getName();
                String value = c.getValue();
                System.out.println("名称:"+name+",值:"+value);
            }
        }
    }
}

image.png

cookie的到期时间

用来指定cookie何时失效,默认为浏览器关闭即失效,可通过setMaxAge(int time)来手动设定cookie的最大有效时间,以秒为单位 到期时间的取值:

  • 负数:表示只在浏览器内存中存活,关闭浏览器窗口即失效
  • 正数:表示cookie对象存活的秒数,浏览器会把cookie保存到硬盘上,就算关闭浏览器或重启电脑,cookie也会存活相应时间
  • 零:表示删除这个cookie,如果原来浏览器已经保存了这个cookie,这样操作后在浏览器内存和客户端中都会删除这个cookie
package com.example.servlet04cookie;

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;

/**
 * Cookie到期时间
 * 负数(默认-1);只在浏览器内部存活,关闭浏览器即失效
 * 正数n:数据在磁盘中存活n秒
 * 0:删除cookie
 */

@WebServlet("/c02")
public class Cookie02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //负整数
        Cookie cookie1 = new Cookie("uname1", "zhangsan");
        cookie1.setMaxAge(-1);//关闭浏览器失效
        resp.addCookie(cookie1);

        //正整数
        Cookie cookie2 = new Cookie("uname2", "lisi");
        cookie2.setMaxAge(30);//存活三十秒
        resp.addCookie(cookie2);

        //0
        Cookie cookie3 = new Cookie("uname3", "wangwu");
        cookie3.setMaxAge(0);//删除cookie
        resp.addCookie(cookie3);
    }
}

cookie注意事项

  • cookie只在当前浏览器中有效,不能跨浏览器或跨电脑
  • 同名cookie会覆盖原有的cookie
  • cookie的存储是有限的,取决于浏览器,4kb左右
  • cookie存中文需要进行编码和解码
package com.example.servlet04cookie;

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;

/**
 * Cookie的创建和发送(响应)和获取
 * 创建:new一个cookie对象
 * 发送:resp.addCookie
 * 获取:req.getCookies
 * 获取cookie是返回一个cookie数组
 */

@WebServlet("/c01")
public class Cookie01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //Cookie的创建
        Cookie cookie = new Cookie("name", "admin");
        //Cookie的发送
        resp.addCookie(cookie);

        //获取Cookie数组
        Cookie[] cookies = req.getCookies();
        //判断Cookie是否为空
        if (cookies != null && cookies.length > 0) {
            //遍历Cookie数组
            for (Cookie c : cookies) {
                //获取并打印cookie的名称和值
                String name = c.getName();
                String value = c.getValue();
                System.out.println("名称:"+name+",值:"+value);
            }
        }
    }
}

cookie的路径

setPath设置cookie的路径,决定了服务器的请求是否会从浏览器中加载某些cookie 当访问的路径包含了cookie的路径时,则该请求有cookie;若访问路径不包含cookie路径,则请求没有cookie

  • 当前服务器下任何项目都可获取cookie对象
  • 当前项目下的资源可获取cookie对象(默认)
  • 指定项目下的资源可获取cookie对象
  • 指定目录下的资源可获取cookie对象
package com.example.servlet04cookie;

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.URLDecoder;
import java.net.URLEncoder;

/**
 * Cookie路径
 */

@WebServlet("/c04")
public class Cookie04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //当前服务器下任何项目都可获取cookie对象,设置路径为"/"
        Cookie cookie01=new Cookie("cookie01","cookie01");
        cookie01.setPath("/");
        resp.addCookie(cookie01);

        //当前项目下的资源可获取cookie对象(默认),默认不设置path或设置为当前站点名
        Cookie cookie02=new Cookie("cookie02","cookie02");
        cookie02.setPath("/cook");
        resp.addCookie(cookie02);

        //指定项目下的资源可获取cookie对象,设置路径为指定站点名
        Cookie cookie03=new Cookie("cookie03","cookie03");
        cookie03.setPath("/sr03");
        resp.addCookie(cookie03);

        //指定目录下的资源可获取cookie对象,设置路径为指定站点名
        Cookie cookie04=new Cookie("cookie04","cookie04");
        cookie04.setPath("/cook/c02");
        resp.addCookie(cookie04);
    }
}

HttpSession对象

又称为会话,本身是HTTP协议中的内容,是一个纯粹的接口,不像HttpServletRequest或HttpServletResponse存在父接口。
对于服务器而言,每一个连接到它的客户端都是一个session(因此无法在新浏览器中获取之前设置的session),保留指定时间段的来自一个用户的多个请求。session的作用是为了表示一次会话或者说确认一个用户,并在一次会话期间共享数据,可通过req.getSession()方法来获取当前会话的session对象。

创建和常用方法

session是为了标识一次会话,那么每次会话有唯一的标志,就是标识符JSESSIONID,是一个特殊的cookie,有效时间为关闭浏览器
每次请求到达服务器,服务器会查看是否从客户端传来一个名为JSESSIONID的cookie,如果没有则认为这是一次新的会话,并创建新的session对象和id作为此次会话的标识;若找到了session对象则认为这是之前标记过的一次会话,返回session对象,数据达到共享。

package com.example.servlet05session;

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;

/**
 * Session对象
 * 获取:
 *      req.getSession();
 *      获取session对象时会先判断session对象是否存在,若存在则获取session对象,若不存在则创建session对象
 * 常用方法:
 *      获取session会话标识符     session.getId()
 *      获取session创建时间      session.getCreationTime()
 *      获取最后一次访问时间       session.getLastAccessedTime()
 *      判断是否是新的session对象  session.isNew()
 */

@WebServlet("/s01")
public class Session01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取session对象
        HttpSession session = req.getSession();

        //获取session对话标识符
        System.out.println(session.getId());
        //获取session的创建时间
        System.out.println(session.getCreationTime());
        //获取最后一次访问时间
        System.out.println(session.getLastAccessedTime());
        //判断是否是新的session对象
        System.out.println(session.isNew());
    }
}

image.png

session域对象

一次会话中数据是可以共享的,因此session作为域对象存在

  • 添加域对象:session.setAttribute()
  • 获取域对象:request.getSession().getAttribute()
  • 删除域对象:session.removeAttribute()
package com.example.servlet05session;

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;

/**
 * Session域对象
 *      添加域对象:session.setAttribute()
 *      获取域对象:request.getSession().getAttribute()
 *      删除域对象:session.removeAttribute()
 *
 * 请求转发:一次请求,session作用域有效,request作用域有效
 * 重定向:两次请求,session作用域有效,request作用域无效
 */

@WebServlet("/s02")
public class Session02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /* session域对象*/
        //获取session对象
        HttpSession session = req.getSession();
        //添加session域对象
        session.setAttribute("uname","admin");
        session.setAttribute("upwd","123456");
        //删除session域对象
        session.removeAttribute("uname");

        /*request域对象*/
        //添加request域对象
        req.setAttribute("name","zhangsan");

        //请求转发跳转到jsp页面
        req.getRequestDispatcher("test.jsp").forward(req, resp);
    }
}

下面是test.jsp代码
<%--
  Created by IntelliJ IDEA.
  User: 15997
  Date: 2024/6/10
  Time: 下午3:18
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>获取域对象</title>
</head>
<body>
<%
    //获取session域对象
    String uname=(String) request.getSession().getAttribute("uname");
    String upwd=(String) request.getSession().getAttribute("upwd");

    //获取request域对象
    String name=(String) request.getAttribute("name");

    //输出
    System.out.println("uname:"+uname+" upwd:"+upwd+" name:"+name);
%>
</body>
</html>

image.png

session对象的销毁

tomcat中无操作时session的默认存活时间为30min,可以在conf目录下的web.xml文件中进行修改默认存活时间
image.png 我们也可以在程序中自己设定session的生命周期或立即销毁

package com.example.servlet05session;

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;

/**
 * Session对象的销毁
 * 1. 默认销毁时间,在web.xml中修改
 * 2. 自行设置销毁时间
 *      设置:session.setMaxInactiveInterval(n)
 *      查看:getMaxInactiveInterval()
 * 3. 立即销毁(常用):session.invalidate()
 * 4. 关闭浏览器就销毁:
 *      session浏览器底层依赖cookie,cookie对象默认只在浏览器内存中存活,关闭浏览器就失效,因为没有JSESSIONID与之对应
 * 5. 关闭服务器时销毁
 */
@WebServlet("/s03")
public class Session03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取session对象
        HttpSession session = req.getSession();

        //获取session对象最大不活动时间
        //System.out.println("session对象最大不活动时间为:"+session.getMaxInactiveInterval());
        //修改最大不活动时间,单位为秒
        //session.setMaxInactiveInterval(15);

        //立即销毁
        //session.invalidate();
    }
}

ServletContext对象

每一个web应用都有且仅有一个ServletContext对象,又称为Application对象。
作用:

  • 作为域对象来共享数据,此数据在整个应用程序中共享
  • 该对象中保存了当前应用程序相关信息,如服务器信息,资源路径等

获取和常用方法

package com.example.servlet06context;

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;

/**
 * 获取ServleetContext对象:
 *      1. 通过request对象获取:req.getServletContext()
 *      2. 通过session对象获取:req.getSession().getServletContext()
 *      3. 通过ServletConfig对象获取:getServletConfig().getServletContext();
 *      4. 直接获取:getServletContext();
 * 常用方法:
 *      1. 获取当前服务器的版本信息:req.getServletContext().getServerInfo()
 *      2. 获取项目的真实路径:req.getServletContext().getRealPath("/")
 */

@WebServlet("/c01")
public class Context01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过request对象获取
        ServletContext servletContext1 = req.getServletContext();

        //通过session对象获取
        ServletContext servletContext2 = req.getSession().getServletContext();

        //通过ServletConfig对象获取
        ServletContext servletContext3 = getServletConfig().getServletContext();

        //直接获取,只能在Servlet中用
        ServletContext servletContext4 = getServletContext();


        //获取当前服务器的版本信息
        String serverInfo = req.getServletContext().getServerInfo();
        System.out.println("当前服务器的版本信息:" + serverInfo);

        //获取项目的真实路径
        String realPath = req.getServletContext().getRealPath("/");
        System.out.println("获取项目的真实路径:" + realPath);
    }
}

image.png

ServletContext域对象

ServeltContext中存的数据可以整个应用程序共享,不手动移除的话会一直保存,关闭服务器后失效
使用方法和request和session域对象一样

image.png

package com.example.servlet06context;

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;

/**ServletContext域对象
 * 用法同request或session,不同的是在整个应用程序中有效,服务器关闭后失效
 */

@WebServlet("/c01")
public class Context02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext servletContext = req.getServletContext();

        //添加域对象
        servletContext.setAttribute("name","zhangsan");

        //获取域对象(在请求转发或重定向的页面中)
        String name=(String)servletContext.getAttribute("name");

        //移除域对象
        servletContext.removeAttribute("name");
    }
}

文件上传和下载

文件上传

涉及到前台页面的编写和后台服务器端代码的编写。前台发送文件,后台接收并保存文件

前台页面

使用表单实现文件上传,表单的请求方式为POST,类型为enctype="multipart/form-data"意思是设置表单的类型为文件上传表单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<!--
文件上传前台:
1. 准备表单
2. 设置表单提交类型为POST method="post"
3. 设置表单类型为文件上传表单 enctype="multipart/form-data"
4. 设置文件提交地址
5. 准备表单元素
    1.普通表单项 type="text"
    2.文件项 type="file"
6. 设置表单元素的name属性值,表单提交一定要设置表单元素的name属性值,否则后台无法接受数据
-->
<form method="post" enctype="multipart/form-data" action="uploadServlet">
    姓名:<input type="text" name="uname"><br>
    文件:<input type="file" name="myfile"><br>
    <!--  button默认的类型时提交类型 type="submit"  -->
    <button>提交</button>
</form>
</body>
</html>

image.png

后台实现

使用注解@MultipartConfig将一个servlet标记为支持文件上传,servlet将multipart/form-data的post请求封装成Part对象,通过Part对上传的文件进行操作

package com.example.servlet07;

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;

/**
 * 文件上传后台:
 * 使用注解@MultipartConfig将一个servlet标记为支持文件上传
 * servlet将multipart/form-data的post请求封装成Part对象,通过Part对上传的文件进行操作
 */

@WebServlet("/uploadServlet")
@MultipartConfig//如果是文件上传,必须设置该注解
public class UploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("文件上传...");
        //设置请求的编码格式
        req.setCharacterEncoding("utf-8");

        //获取普通表单项(获取参数)
        String uname = req.getParameter("uname");//表单中表单元素的name属性值
        System.out.println("uname:"+uname);

        //获取Part对象
        Part part=req.getPart("myfile");//表单中file文件域的name属性值
        //通过Part对象得到上传的文件名
        String fileName=part.getSubmittedFileName();
        System.out.println("上传文件名:"+fileName);
        //得到文件存放的路径
        String filePath=req.getServletContext().getRealPath("/");
        System.out.println("文件存放的路径:"+filePath);
        //上传文件到指定目录
        part.write(filePath+"/"+fileName);
    }
}

image.png

文件下载

将服务器上的资源下载到本地,有两种方式,分别是超链接和后台代码下载

超链接下载

在html或jsp页面中使用a标签时,原意是希望进行跳转,但超链接遇到浏览器不识别的资源时会自动下载,遇到浏览器能识别的资源时会默认显示出来;也可以通过download属性规定浏览器进行下载(有些浏览器不支持)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>

<!--
超链接下载:
    当使用超链接(a标签)时,如果遇到浏览器能够识别的资源,则会显示内容,如果遇到浏览器不能识别的资源,则会进行下载
download属性:
    通过download属性规定浏览器进行下载
    download属性可以不写任何信息,会自动使用默认文件名,如果设置了download属性的值,则使用设置的值作为文件名
-->

<!--浏览器中能识别的资源-->
<a href="download/TEST.txt">文本文件</a>
<a href="download/微信图片_20220703123741.jpg">图片文件</a>
<!--浏览器中不能识别的资源-->
<a href="download/笔记.zip">压缩文件</a>
<hr>
<a href="download/TEST.txt" download>文本下载</a>
<a href="download/微信图片_20220703123741.jpg" download="狗狗.jpg">图片下载</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方法向客户端写入附件内容。
package com.example.servlet07;

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.*;

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

@WebServlet("/downloadServlet")
public class downloadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("文件下载...");

        //设置请求编码格式
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //获取参数(得到要下载的文件名)
        String fileName = req.getParameter("fileName");
        //参数的非空判断 trim()去除字符串前后空格
        if (fileName == null || fileName.trim().length() == 0) {
            resp.getWriter().write("请输入要下载的文件名");
            resp.getWriter().close();
            return;
        }
        //得到图片存放的路径
        String path = req.getServletContext().getRealPath("/download/");
        //通过路径得到file对象
        File file = new File(path + fileName);
        //判断文件对象是否存在且是一个标准文件
        if (file.exists() && file.isFile()) {
            //设置响应类型(浏览器无法激活处理的类型)
            resp.setContentType("application/octet-stream");
            //设置响应头信息
            resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            //得到file文件输入流
            InputStream in = new FileInputStream(file);
            //得到字节输出流
            ServletOutputStream out = resp.getOutputStream();
            //定义byte数组
            byte[] bytes = new byte[1024];
            //定义长度
            int len = 0;
            //循环输出
            while ((len = in.read(bytes)) != -1) {
                //输出内容
                out.write(bytes, 0, len);
            }
            //关闭资源
            out.close();
            in.close();
        } else {
            resp.getWriter().write("文件不存在请重试");
            resp.getWriter().close();
        }
    }
}

下面是download.html代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>

<!--后台代码下载-->
<form action="downloadServlet">
    文件名:<input type="text" name="fileName" placeholder="请输入要下载的文件名">
    <button>下载</button>
</form>

</body>
</html>