云计算百科
云计算领域专业知识百科平台

一天学完Servlet!!!(万字总结)

文章目录

    • 前言
    • Servlet打印Hello Servlet
    • Servlet生命周期
  • HttpServletRequest对象
    • 常用api方法
    • 请求乱码问题
    • 请求转发
    • request域对象
  • HttpServletResponse对象
    • 响应数据
    • 响应乱码问题
    • 请求重定向
    • 请求转发与重定向区别
  • Cookie对象
    • Cookie的创建与获取
    • Cookie设置到期时间
    • Cookie注意点
    • Cookie的路径
  • HttpSession对象
    • Session对象获取和常用Api
    • 标识符JSESSIONID
    • session域对象
    • session对象的销毁
  • ServletContext对象
    • 常用aip
    • ServletContext域对象
  • Servlet三大域对象总结
  • Servlet文件上传
    • 前端页面实现
    • 后台代码实现
  • Servlet文件下载
    • 超链接下载
    • 后台代码下载

前言

虽然Servlet现在已经不会直接用于开发了,但是作为SpringMVC的前置知识,还是需要全面了解一下的,故做此文,用于总结。

Servlet打印Hello Servlet

1、前置配置

下载Tomcat : Tomcat下载连接

idea2022 创建Servlet项目流程:

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

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!!!");
}
}

运行效果: 浏览器: 在这里插入图片描述 控制台: 在这里插入图片描述

Servlet生命周期

生命周期简单来说,分为3步, 初始化(init()) ==> 调用方法(service()) ==>销毁方法(destroy()) 放到代码中来看:

@WebServlet("/ser02")
public class servlet02 extends HttpServlet {

private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

/**
* 系统方法,服务器自动调用,只执行一次
* @throws ServletException
*/

@Override
public void init() throws ServletException {
System.out.println("Servlet被创建了…" + formatter.format(new Date(System.currentTimeMillis())));
}

/**
* 每次调用都会执行
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet被调用了…" + formatter.format(new Date(System.currentTimeMillis())));
}

/**
* 系统方法,服务器自动调用,只执行一次
*/

@Override
public void destroy() {
System.out.println("Servlet被销毁了…" + formatter.format(new Date(System.currentTimeMillis())));
}
}

运行结果: 在这里插入图片描述

从图中可以看出,不管浏览器访问调用多少次,初始化和销毁方法只会执行一次,但是Service在每次访问都会执行。

HttpServletRequest对象

简单来讲,HttpServletRequest就是封装用户传过来的请求信息,对象由Tomcat封装好传过来,同时该对象是一个接口。service() 方法中传递的HttpServletRequest对象是该接口的实例化对象。

常用api方法

@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 完整的请求url
String url = req.getRequestURL().toString();
System.out.println("获取客户端请求的完整url: " + url);
// 2. 部分url(站点名开始,到?前面结束)
String uri = req.getRequestURI();
System.out.println("客户端请求的部分url: " + uri);
// 3. 获取请求行中的参数部分
String queryString = req.getQueryString();
System.out.println("获取请求行中的参数部分: " + queryString);
// 4. 获取客户端请求方式
String method = req.getMethod();
System.out.println("获取客户端的请求方式: " + method);
// 5. 获取http版本号
String protocol = req.getProtocol();
System.out.println("http版本号为: " + protocol);
// 6. 获取webapp名字
String contextPath = req.getContextPath();
System.out.println("webapp名字为: " + contextPath);
// 7. 获取请求参数
// (1)、获取指定名称参数
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
System.out.println("username: " + username + " pwd: " + pwd);
// (2)、获取指定名称参数的所有值
String[] hobbys = req.getParameterValues("hobby");
System.out.println("hobbys: " + Arrays.toString(hobbys));
if(hobbys != null && hobbys.length > 0) {
for (String hobby : hobbys) {
System.out.println("爱好: " + hobby);
}
}
}
}

请求url:

http://localhost:8080/s01/ser01?username=suxuchao&pwd=123456&hobby=sing&hobby=dance&hobby=computer

运行结果:

获取客户端请求的完整url: http://localhost:8080/s01/ser01
客户端请求的部分url: /s01/ser01
获取请求行中的参数部分: username=suxuchao&pwd=123456&hobby=sing&hobby=dance&hobby=computer
获取客户端的请求方式: GET
http版本号为: HTTP/1.1
webapp名字为: /s01
username: suxuchao pwd: 123456
hobbys: [sing, dance, computer]
爱好: sing
爱好: dance
爱好: computer

请求乱码问题

在Tomcat8以上,GET请求有中文的话不会出现乱码,但是使用POST请求就会出现乱码,如下:

代码:

@WebServlet("/ser03")
public class Servlet03 extends HttpServlet {

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取客户端传递的参数
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
String method = req.getMethod();
System.out.println("请求方法为: " + method);
System.out.println("username: " + username);
System.out.println("pwd: " + pwd);
}
}

浏览器输入: 在这里插入图片描述

运行结果:

请求方法为: POST
username: å¼ ä¸‰
pwd: å¼ ä¸‰

解决方法: 首先我们需要知道,乱码是因为在解析过程中,request默认使用的编码是ISO-8859-1 (此编码不支持中文),所以解析一定会有乱码

//设置编码
req.setCharacterEncoding("UTF-8");

将该代码放在方法一开始,即可解决中文乱码问题

更正后结果:

请求方法为: POST
username: 张三
pwd: 张三

请求转发

请求转发有几个特点

  • 服务端行为
  • 地址栏的url不发生变化
  • 从始至终只有一个请求
  • request数据可以共享
  • 代码中getRequestDispatcher只能转发一次
  • 请求url:

    http://localhost:8080/s01/ser03?username=张三

    Servlet3代码:

    String username = req.getParameter("username");
    System.out.println("Servlet03: " + username);

    //请求转发跳转到Servlet04
    req.getRequestDispatcher("ser04").forward(req, resp);

    Servlet4代码:

    // 接收客户端请求的参数
    String username = req.getParameter("username");
    System.out.println("Servlet04: " + username);

    运行结果:

    Servlet03: 张三
    Servlet04: 张三

    同时,重定向可以重定向.html/.jsp文件等页面:

    req.getRequestDispatcher("login.jsp").forward(req, resp);

    request域对象

    request域对象中的数据在一次请求中有效,经过请求转发,request域中的数据仍然存在,在请求转发过程中可以通过request来传输/共享数据。

    Servlet05代码:

    System.out.println("Servlet05…");
    //设置域对象内容
    req.setAttribute("name", "admin");
    req.setAttribute("age", 18);
    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    req.setAttribute("list", list);
    req.getRequestDispatcher("ser06").forward(req, resp);

    Servlet06代码:

    System.out.println("Servlet06…");
    //获取域对象内容
    String name = (String) req.getAttribute("name");
    Integer age = (Integer) req.getAttribute("age");
    List<String> list = (List<String>) req.getAttribute("list");
    System.out.println("name: " + name);
    System.out.println("age: " + age);
    System.out.println("list: " + Arrays.toString(list.toArray()));

    请求url:

    http://localhost:8080/s01/ser05

    访问结果:

    Servlet05…
    Servlet06…
    name: admin
    age: 18
    list: [aaa, bbb]

    HttpServletResponse对象

    简单来讲,HttpServletResponse就是封装服务器处理后,要响应给客户端的数据,封装的数据有 (发送数据,发送响应头,发送响应状态码) 的方法。service() 方法中传递的HttpServletResponse对象是该接口的实例化对象

    响应数据

    使用字符流:

    // 获取字符输出流
    PrintWriter writer = resp.getWriter();
    // 输出数据
    writer.write("Hello");

    使用字节流:

    //字节输出流
    ServletOutputStream outputStream = resp.getOutputStream();
    outputStream.write("Hi".getBytes());

    注意: 两个流只能选择一个来用,否则就会报错。

    响应乱码问题

    s02代码:

    PrintWriter writer = resp.getWriter();
    writer.write("<h2>你好</h2>");

    s03代码:

    ServletOutputStream os = resp.getOutputStream();
    os.write("<h2>你好</h2>".getBytes("UTF-8"));

    运行结果:

    <h2>??</h2>
    <h2>浣犲ソ</h2>

    解决方式: 在代码最开头添加一行这个代码,类比request的解决方法

    resp.setCharacterEncoding("UTF-8");

    但是我们发现此时两种输出流打印的结果最后都变成了相同的乱码,如下:

    <h2>浣犲ソ</h2>

    这是因为我们只设置了服务端的编码,request请求数据只在服务端之间传递,因此这样设置可以解决问题,但是response格式的话,我们不但要设置服务端,还有设置客户端的编码,解决方法如下:

    // 设置服务端的编码
    resp.setCharacterEncoding("UTF-8");
    // 设置客户端的编码
    resp.setHeader("content-type", "text/html;charset=UTF-8");

    或者更简单的解决方法

    //同时设置编码
    resp.setContentType("text/html;charset=UTF-8");

    这样就可以解决乱码和无法识别html的问题了。

    请求重定向

  • 重定向是客户端行为,由服务端指导
  • 地址栏url会发生改变
  • 存在两次请求
  • 两次请求的数据不共享
  • Servlet04代码:

    System.out.println("Servlet04….");
    resp.setContentType("text/html;charset=UTF-8");
    String username = req.getParameter("username");
    System.out.println("username: " + username);
    resp.sendRedirect("s05");

    Servlet05代码:

    System.out.println("Servlet05….");
    resp.setContentType("text/html;charset=UTF-8");
    String username = req.getParameter("username");
    System.out.println("username: " + username);

    第一次请求url:

    http://localhost:8080/s01/s04?username=zhangsan

    重定向后的url:

    http://localhost:8080/s01/s05

    打印结果:

    Servlet04….
    username: zhangsan
    Servlet05….
    username: null

    请求转发与重定向区别

    在这里插入图片描述

    Cookie对象

    Cookie就是通过浏览器的一些程序,将只在客户端进行操作的数据,存在cookie中,以此来减轻服务器的压力,例如:记住密码的操作。 cookie就是简单的键值对形式存储,key和value之间用"="连接,不同kv之间用“;” 来分割。

    Cookie的创建与获取

    创建Cookie对象

    // Cookie的创建
    Cookie cookie = new Cookie("username", "zhangsan");
    // 发送(响应)Cookie对象
    resp.addCookie(cookie);

    在这里插入图片描述

    获取Cookie对象:

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

    运行结果:

    name: username
    value: zhangsan

    Cookie设置到期时间

    如下:

    // 到期时间为负数(浏览器关闭就失效)
    Cookie cookie1 = new Cookie("username01", "zhangsan");
    cookie1.setMaxAge(1);
    res.addCookie(cookie1);

    // 到期时间为整数(cookie存在指定时间)
    Cookie cookie2 = new Cookie("username02", "lisi");
    cookie2.setMaxAge(30);
    res.addCookie(cookie2);

    // 到期时间为0(删除cookie)
    Cookie cookie3 = new Cookie("username03", "wangwu1");
    cookie3.setMaxAge(0);
    res.addCookie(cookie3);

    Cookie注意点

  • Cookie只在当前浏览器有用,Cookie无法跨浏览器,且只保存在当前电脑

  • Cookie存中文问题

  • // 使用URLEncoder进行编码
    String name = "姓名";
    String value = "张三";
    name = URLEncoder.encode(name);
    value = URLEncoder.encode(value);
    // 创建Cookie对象
    Cookie cookie1 = new Cookie(name, value);
    resp.addCookie(cookie1);

    //获取Cookie
    Cookie[] cookies = req.getCookies();
    if(cookies != null && cookies.length > 0) {
    for (Cookie cookie : cookies) {
    String dname = URLDecoder.decode(cookie.getName());
    String dvalue = URLDecoder.decode(cookie.getValue());
    System.out.println("dname: " + dname);
    System.out.println("dvalue: " + dvalue);
    }
    }

    存储效果:在这里插入图片描述

  • 同名Cookie问题
  • 如果服务端发送重复的Cookie,那么会覆盖原有的Cookie.

  • 浏览器存放Cookie的数量
  • 浏览器对于Cookie的存放数量也是有上限的,Cookie存放在浏览器,而且一般由服务端创建和设定,后期结合Session来进行会话追踪。

    Cookie的路径

    简单总结,如果访问路径中带上了Cookie设置的路径,就可以访问到Cookie,否则就访问不到。

    http://localhost:8080/s01/cookie01

    对于上述url, 如果Cookie设置如下就可以访问到

    cookie.setPath("/");
    cookie.setPath("/s01");
    cookie.setPath("/s01/cookie01");

    如果设置如下就访问不到

    cookie.setPath("/s02");
    cookie.setPath("/s01/cookie02");

    HttpSession对象

  • Session 数据本身只存储在服务端
  • 客户端仅持有用于查找 Session 的 ID(相当于钥匙)
  • 若客户端禁用 Cookie,需通过 URL 参数传递 Session ID
  • 每一个Session只在当前浏览器保存,开新浏览器就无法获得当前Session
  • Session对象获取和常用Api

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

    运行结果:

    A07655B90CBD6033B4D2F4FCC3C5FBE3
    1745488955622
    1745488955622
    true

    标识符JSESSIONID

    服务器每次会先检查从客户端传过来数据中有没有JSESSIONID标识符,该标识符存放于cookie,服务器收到JSESSIONID之后会先查看是否含有该值的Session对象,如果没有,则认为这是一次新对话,创建一个新Session对象,如果由,就认为是标志过的会话,返回该Session对象,实现数据共享。在这里插入图片描述

    session域对象

    整体与request的域对象的api是一样的,但是与request域对象的区别是,Session域对象只要你这次会话没有结束,即浏览器没有和服务器断开,无论开多少个页面都能访问到Session中的数据,request则是只要换了一次请求,就无法共享域对象数据。

    // 获取Session域对象
    HttpSession session = req.getSession();
    // 设置域对象
    session.setAttribute("username", "zhangsan");
    session.setAttribute("pwd", "123456");
    //移除域对象
    session.removeAttribute("pwd");
    //获取域对象
    String username = (String) session.getAttribute("username");

    session对象的销毁

  • 默认销毁时间 Tomcat/config/web.xml,在该文件下,默认session在不操作的情况下,存活时间为30min。
  • <session-config>
    <session-timeout>30</session-timeout>
    </session-config>

  • 自己设定销毁时间
  • HttpSession session = req.getSession();
    session.setMaxInactiveInterval(15); // 15秒过期

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

  • 关闭浏览器失效
  • Session 底层依赖Cookie来实现,而Cookie存在浏览器内存中,因此关闭浏览器导致Cookie失效,也会导致Session失效。

    ServletContext对象

  • 每个web程序,有且仅有一个Servlet Context对象,又称Application对象,在WEB程序启动后,一般会创建一个对应的ServletContext对象。
  • 对象中保存了当前服务器的信息还有getRealPath获取资源真实路径等。
  • 常用aip

    代码:

    // 获取servletContext对象
    // (1)、通过request对象获取
    ServletContext servletContext1 = req.getServletContext();
    // (2)、通过Session对象获取
    ServletContext servletContext2 = req.getSession().getServletContext();
    // (3)、通过ServletConfig对象获取
    ServletConfig servletConfig = getServletConfig();
    ServletContext servletContext3 = servletConfig.getServletContext();
    // (4)、直接获取
    ServletContext servletContext4 = getServletContext();

    //常用方法
    //1. 获取当前服务器的版本信息
    String serverInfo = req.getServletContext().getServerInfo();
    System.out.println("获取当前服务器的版本信息: " + serverInfo);
    //2. 获取当前项目真实路径
    String realPath = req.getServletContext().getRealPath("/");
    System.out.println(" 获取当前服务器的真实路径: " + realPath);

    运行结果:

    获取当前服务器的版本信息: Apache Tomcat/8.5.47
    获取当前服务器的真实路径: D:\\code_study\\servlet01\\target\\servlet01-1.0-SNAPSHOT\\

    ServletContext域对象

    该域对象数据不建议存过多数据,因为如果不手动移除,服务器启动后会一直存在。

    ServletContext servletContext = req.getServletContext();
    // 设置域对象
    servletContext.setAttribute("name", "zhangsan");
    // 获取域对象
    String name = (String) servletContext.getAttribute("name");
    // 移除域对象
    System.out.println("name = " + name);
    servletContext.removeAttribute("name");

    Servlet三大域对象总结

  • request域对象 在一次request请求中生效。
  • Session域对象 在一次会话中生效。
  • ServletContext域对象 在服务器启动期间一直生效,除非重启服务器。
  • Servlet文件上传

    前端页面实现

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    </head>
    <body>
    <!
    文件上传
    1.准备表单
    2.设置表单的提交类型
    3.设置表单类型问文件上传表单 enctype="multipart/form-data"
    4.设置文件提交的地址
    5.准备表单元素
    1. 普通的表单项 type="text"
    2. 文件项 type="file"
    >
    <form method="post" enctype="multipart/form-data" action="uploadServlet">
    姓名: <input type="text" name="username"> <br>
    文件: <input type="file" name="myfile"> <br>
    <button>提交</button>
    </form>
    </body>
    </html>

    后台代码实现

    @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 username = req.getParameter("username");
    System.out.println("username: " + username);
    // 获取Part对象(Serlet将mutipart/form-data的POST请求封装成Part对象)
    Part part = req.getPart("myfile");
    // 通过part对象得到上传的文件名
    String fileName = part.getSubmittedFileName();
    System.out.println("上传文件名: " + fileName);

    String realPath = req.getServletContext().getRealPath("/");
    System.out.println("文件存放路径: " + realPath);
    // 上传文件到指定目录
    part.write(realPath + "/" + fileName);

    }
    }

    最后文件上传的路径可以改成你想要指定的。 注意:上传文件一定要加@MultipartConfig 注解,否则文件无法上传成功

    Servlet文件下载

    就是将浏览器上的文件下载到本地

    超链接下载

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>文件下载</title>
    </head>
    <body>
    <!
    浏览器可识别的文件可以直接浏览器中查看,
    不可识别的文件需要先浏览器下载。
    >
    <!浏览器可以识别的资源>
    <a href="download/hello.txt">文本文件</a>
    <a href="download/ai.jpg">图片文件</a>
    <!浏览器不可以识别的资源>
    <a href="download/yasuo.zip">压缩文件</a>
    <hr>
    <!添加了download属性,可以直接在浏览器中下载对应文件>
    <a href="download/hello.txt" download>文本文件</a>
    <a href="download/ai.jpg" download="ai.jpg">图片文件</a>
    </body>
    </html>

    后台代码下载

    @WebServlet("/downloadServlet")
    public class DownloadServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("文件下载开始….");

    // 1. 设置请求的编码
    req.setCharacterEncoding("UTF-8");
    // 2. 获取参数
    String fileName = req.getParameter("fileName");
    // 3. 参数的非空判断
    if(fileName == null || "".equals(fileName.trim())) {
    resp.setContentType("text/html;charset=utf-8");
    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()) {
    // 设置相应类型(浏览器无法处理或激活某个程序来处理的MIME类型)
    resp.setContentType("application/x-msdownload");
    // 设置响应头
    resp.setHeader("content-Disposition", "attachment;filename" + fileName);
    // 得到file文件输入流
    InputStream is = new FileInputStream(file);
    // 得到字节输出流
    ServletOutputStream os = resp.getOutputStream();
    // 定义字节数组
    byte[] bytes = new byte[1024];
    //定义长度
    int len = 0;
    // 循环输出
    while((len = is.read(bytes)) != 1) {
    // 输出
    os.write(bytes, 0, len);
    }
    // 关闭资源
    os.close();
    is.close();
    } else {
    resp.getWriter().write("文件不存在,请重试!");
    resp.getWriter().close();
    }
    }
    }

    至此,Servlet基本流程就学习结束。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 一天学完Servlet!!!(万字总结)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!