Tomcat和Servlet学习记录

news/2024/7/5 3:22:46

Tomcat和Servlet入门

  • 1.Tomcat
    • 1.1什么是Tomcat
    • 1.2 目录结构
    • 1.3 IDEA中配置Tomcat
  • 2.Servlet
    • 2.1 什么是Servlet
    • 2.2 使用Servlet获取参数
    • 2.3 Servlet生命周期
    • 2.4 Servlet.http.HttpServlet
  • 3.Http协议
  • 4.Session 和Cookie
    • 4.1 Session
    • 4.2 Cookie
  • 5.服务器端内部转发和客户端重定向
  • 6.保存作用域(request、session、application)

1.Tomcat

1.1什么是Tomcat

简单来说,Tomcat是Web服务器

1.2 目录结构

在这里插入图片描述
bin目录: 可执行文件,主要有两大类,一类是以.sh结尾的(linux命令)另一类是以.bat结尾的(windows命令)。
startup 用来启动tomcat。shutdown 用来关闭tomcat。

conf目录: 配置文件。server.xml该文件用于配置server相关的信息,比如tomcat启动的端口号。tomcat-user.xml配置用户名密码和相关权限。

lib目录: 主要用来存放tomcat运行需要加载的jar包。

logs目录: 用来存放tomcat在运行过程中产生的日志文件。windows环境中,控制台的输出日志catalina.xxxx-xx-xx.log文件中 。linux环境中,控制台的输出日志在catalina.out文件中。

temp目录: 用户存放tomcat在运行过程中产生的临时文件。

work目录: 用来存放tomcat在运行时的编译后文件,清空work目录后重启tomcat,可以达到清除缓存的作用。

webapps目录: 项目部署目录,当tomcat启动时会去加载webapps目录下的所有应用程序。在浏览器中可以根据自己的需求访问不同的应用程序。
在这里插入图片描述在这里插入图片描述

应用程序一般会打包成归档格式(.war),然后放到Tomcat的应用程序部署目录。而webapp有特定的组织格式,是一种层次型目录结构,通常包含了servlet代码文件、HTML/jsp页面文件、类文件、部署描述符文件等等,相关说明如下:

   /:web应用程序的根目录,可以存放HTML/JSP页面以及其他客户端浏览器必须可见的其他文件(如js/css/图像文件)。在较大的应用程序中,还可以选择将这些文件划分为子目录层次结构。

  /WEB-INF:此webapp的所有私有资源目录,用户浏览器不可能访问到的,通常web.xml和context.xml均放置于此目录。

  /WEB-INF/web.xml:此webapp的私有的部署描述符,描述组成应用程序的servlet和其他组件(如filter),以及相关初始化参数和容器管理的安全性约束。

   /WEB-INF/classes:此webapp自有的Java程序类文件(.class)及相关资源存放目录。

   /WEB-INF/lib:此目录存放webapp自有的JAR文件,其中包含应用程序所需的Java类文件(及相关资源),例如第三方类库或JDBC驱动程序。

1.3 IDEA中配置Tomcat

IDEA中配置Tomcat

2.Servlet

2.1 什么是Servlet

Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
在这里插入图片描述

2.2 使用Servlet获取参数

功能:前端输入相应的参数,使用post请求发送给后端并打印获取到的参数。
在这里插入图片描述
项目目录结构

//addServlet.java 获取前端传来的数据并解析
//同时,后端给前端返回接受成功的文本信息
public class addServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8"); //设置编码格式 防止中文乱码
        String name = req.getParameter("name");
        int price =Integer.parseInt(req.getParameter("price"));
        int count = Integer.parseInt(req.getParameter("count"));
        String remark = req.getParameter("remark");

        System.out.println("name :"+ name);
        System.out.println("price :"+ price);
        System.out.println("count :"+ count);
        System.out.println("remark :"+remark);
        
        // 设置响应的内容类型
        resp.setContentType("text/plain");
        // 获取输出流
        PrintWriter out = resp.getWriter();
        // 向浏览器发送响应数据
        out.println("Data received successfully!");
        out.close(); // 关闭输出流
    }
}
<!--add.html-->
<body>
    <form action="addInformation" method="post">
        名称:<input type="text" name="name"><br/>
        价格:<input type="text" name="price"><br/>
        库存:<input type="text" name="count"><br/>
        备注:<input type="text" name="remark"><br/>
        <input type="submit" value="提交"/>
    </form>
</body>
<!-- web.xml-->
<servlet>
    <servlet-name>AddServlet</servlet-name> <!-- 3-->
    <servlet-class>addServlet</servlet-class><!-- 4-->
</servlet>
<servlet-mapping>
    <servlet-name>AddServlet</servlet-name> <!-- 2-->
    <url-pattern>/addInformation</url-pattern> <!-- 1-->
</servlet-mapping>

执行顺序是:

  • 前端访问add.html界面(即前端向后端请求add.html页面)。后端返回页面给前端。
  • 前端点击添加,触发"addInformation"的动作,会使用“post”向后端的某个组件发送表单。后端会需要有对应的组件处理。
  • web.xml配置:即如何确定action对应的是后端的哪个组件。(也可以直接在addServlet添加注解“/addInformation”表示这个动作与addServlet对应起来。)
  • servlet-name可以随意命名,url-pattern是html中的action,servlet-class要和具体的实现类对应起来

2.3 Servlet生命周期

Servlet接口的定义如下

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}
  • init方法: 当Servlet第一次被请求时,Servlet容器会开始调用这个方法初始化一个Servlet对象,并且这个方法只会在第一次请求时才会被调用,在后续请求中不会在被Servlet容器调用。

  • service方法: 每当请求Servlet时,Servlet容器就会调用这个方法。第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。

  • destory方法: 卸载应用程序或者关闭Servlet容器时,Servlet容器就会调用这个方法,一般在这个方法中会写一些清除代码。

//注:HttpSrvlet中并没有init和destory方法,重写的是GenericServlet类中的init和destory方法,GenericServlet类实现了Servlet接口
public class LifeServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("正在初始化...");
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("正在服务...");
    }
    @Override
    public void destroy() {
        System.out.println("正在销毁....");
    }
}
//打开浏览器,多次刷新,最后终止程序,输出如下
//正在初始化...
//正在服务...
//正在服务...
//正在服务...
//正在销毁...

2.4 Servlet.http.HttpServlet

在这里插入图片描述
一般自定义的Servlet类需要继承HttpServlet类,虽然HttpServlet是抽象类,但是内部仍然实现了许多方法,如:Service方法等

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod(); //获取要使用的方法
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }

调用Service方法时先解析HttpServletRequest中的方法参数,并调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions,doDelete。因此在我们自定义的Servlet中如果我们需要实现具体的服务逻辑,不再需要重写service方法了,只需要覆盖doGet或者doPost或者其他do方法即可。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_post_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(405, msg);
    } else {
        resp.sendError(400, msg);
    }
}

以上述HttpServlet中的doPost方法为例,一般情况下我们自定义的Servlet类会继承HttpServlet类然后重写对应的方法,但是如果没有重写直接调用对应的方法,会报错:405

3.Http协议

HTTP协议详解——请求消息、响应消息、请求方法、响应状态码

在上述的add.html例子中,使用的是POST请求,客户端点击提交按钮后,向服务器端发送请求,可以在如下图的Headers中的Request Headers中查看请求行、请求头,在Payload中查看请求体(POST有但是GET没有)。服务器端接收到数据后会打印输出"Data received successfully",在Response中可以查看到响应体,在Headers的Reponse Headers中查看响应行和响应头。
在这里插入图片描述在这里插入图片描述
补充:响应头说明
在这里插入图片描述

4.Session 和Cookie

4.1 Session

  • 为什么引入session?
    HTTP 是无状态的,即服务器无法判断多个请求时由单个还是多个客户端发起的。以购物车为例,如果有用户A和B,A先发送请求将某物品加入购物车然后退出。B的购物车已经加入了某物品,发送请求对该物品结账,如果服务器无法判断是对A还是B的物品结账,可能会带来混乱。
    因此会话技术(session)诞生,解决了Http无状态的问题。
  • 什么是session?
    服务器为了保存用户状态而创建的一个特殊的对象。
  • session 的实现
    每个session由JSESSIONID唯一标识,JSESSIONID是由服务器端生成的。http在请求的时候会携带 JSESSIONID(如果存在),如果JSESSIONID不存在则表明是客户端第一次请求,此时服务器端会生成唯一的 session并将JSESSIONID返回给客户端,客户端将JSESSIONID保存为cookie,下次请求的时候可以继续携带。
    (1)客户端第一次发起请求
    此时客户端没有JSESSIONID,因此不携带。服务器端收到请求后使用getSession判断这是客户端第一次请求,于是创建session并以cookie的形式将JSESSIONID返回给客户端。客户端收到响应后将JSESSIONID作为cookie保存下来。
    (2)客户端非第一次发起请求
    此时客户端已经有JSESSIONID,因此请求时直接携带。服务器端收到请求后使用getSession判断客户端的session已经被保存了,直接将JSESSIONID对应的session返回给客户端。在这里插入图片描述
    在这里插入图片描述
  • session常用API
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数相同
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

session.getId() -> 获取sessionID
session.isNew() -> 判断当前session是否是新的
session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒
session.setMaxInactiveInterval()
session.invalidate() -> 强制性让会话立即失效
  • session抓包实验
    多次发起请求,服务器端打印sessionID,客户端查看请求和响应。
    参考抓包实验部分
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        String id = session.getId();
        System.out.println(id);
    }
//多次请求发现id是一致的
//92F0B4E22926ED64726E1158547E4E11  22:31:34
//92F0B4E22926ED64726E1158547E4E11  22:31:52

在这里插入图片描述

4.2 Cookie

彻底搞懂cookie
session和cookie的区别

  • cookie保存在客户端,session保存在服务端
  • cookie作用于他所表示的path中(url中要包含path),范围较小。session代表客户端和服务器的一次会话过程,web页面跳转时也可以共享数据,范围是本次会话,客户端关闭也不会消失。会持续到我们设置的session生命周期结束(默认30min)
  • 我们使用session需要cookie的配合。cookie用来携带JSESSIONID
  • cookie存放的数据量较小,session可以存储更多的信息。
  • cookie由于存放在客服端,相对于session更不安全
  • 由于session是存放于服务器的,当有很多客户端访问时,肯定会产生大量的session,这些session会对服务端的性能造成影响。

5.服务器端内部转发和客户端重定向


在这里插入图片描述
上图分别是服务器内部转发和客户端重定向的示意图。

  • 服务器内部转发:客户端给服务器端发送请求后,服务器端将请求转发给其他服务器处理,最终响应给客户端。对于客户端而言,内部经过了多少次转发,客户端是不知道的并且地址栏没有变化
  • 客户端重定向:客户端向服务器端发送请求后,服务器会响应给服务器重定向到其他服务器,此时客户端会自动重新给新的服务器发送请求。对于客户端而言是知道具体是哪个服务器响应的,URL会有变化

6.保存作用域(request、session、application)

(1)request保存作用域
一次请求范围内有效:即只有当次请求才有效,二次请求会无效。例如服务器内部转发,request有效。但是客户端重定向request无效,因为客户端重定向发生了2次请求。

//客户端重定向
//Area1中设置request的值,重定向到Area2.在Area2中获取request的值发现为null
@WebServlet("/RequestSaveArea1")
public class RequestSaveArea1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("num",10);
        resp.sendRedirect("RequestSaveArea2");
    }
}

@WebServlet("/RequestSaveArea2")
public class RequestSaveArea2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object num = req.getAttribute("num");
        System.out.println(num); //null
    }
}
//服务器内部转发
//Area1中设置request的值,转发到Area2.在Area2中可以获取到request的
@WebServlet("/RequestSaveArea3")
public class RequestSaveArea3 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("num",10);
        req.getRequestDispatcher("RequestSaveArea4").forward(req,resp); //重定向
    }
}

@WebServlet("/RequestSaveArea4")
public class RequestSaveArea4 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object num = req.getAttribute("num");
        System.out.println(num); //10
    }
}

(2)session保存作用域
会话期内有效:session创建后,保存的资源一直有效,直到session终止。session终止的方法:调用了session.invalidate()方法、session过期(Tomcat默认30分钟)、服务器重新启动

(3)application保存作用域
一次应用程序范围有效:即只要服务器不重启,保存的资源都有效。其他客户端也可以访问该资源。

@WebServlet("/ApplicationSaveArea1")
public class ApplicationSaveArea1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext application = req.getServletContext();//获取上下文
        application.setAttribute("num",10);//保存值
        resp.sendRedirect("ApplicationSaveArea2");//重定向

    }
}

@WebServlet("/ApplicationSaveArea2")
public class ApplicationSaveArea2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext application = req.getServletContext();
        Object num = application.getAttribute("num");
        System.out.println(num);//10
    }
}

http://lihuaxi.xjx100.cn/news/2056306.html

相关文章

Mybatis-plus自定义模板生成代码

Mybatis-plus自定义模板生成代码 参考&#xff1a; 自定义ID生成器 | MyBatis-Plus 自定义属性注入到模板 - MyBatis Plus 教程 MyBatis Plus 代码生成器&#xff08;最新版本&#xff1a;V3.5.3&#xff09;使用指南 - 掘金 mybatis plus generator 3.5.1 从了解到自定义…

Go语言必知必会100问题-11 使用选项模式

使用选项模式 在设计API时&#xff0c;可能会遇到一个问题&#xff1a;如何处理可选配置&#xff1f;有效的解决可选配置问题可以提高API的灵活性。本文通过一个具体示例说明处理可选配置的一些方法。该示例的要求是设计一个对外提供创建HTTP服务器的库函数。函数定义如下&…

[MYSQL数据库]--mysql的基础知识

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、数据库…

优思学院|质量工程师需要学习什么软件?

初入职质量工程师的朋友常常会问&#xff1a;质量工程师需要学习什么软件&#xff1f;在质量控制和管理的世界里&#xff0c;拥有强大的数据分析工具是走向成功的关键&#xff0c;因此&#xff0c;对于质量工程师来说&#xff0c;掌握正确的软件不仅能提升工作效率&#xff0c;…

ad18学习笔记十六:如何放置精准焊盘到特定位置,捕抓功能的讲解

网上倒是一堆相关的指导 AD软件熟练度提升&#xff0c;如何设置板框捕捉&#xff1f;_哔哩哔哩_bilibili 关于Altium Designer 20 的捕抓功能的讲解_ad捕捉设置-CSDN博客 AD软件捕捉进阶实例&#xff0c;如何精确的放置布局元器件&#xff1f;_哔哩哔哩_bilibili AD绘制PCB…

MySQLBackup备份数据库

环境&#xff1a;MySQL 8.0.36 1、安装部署 [rootnode5 ~]# wget -c https://edelivery.oracle.com/osdc/softwareDownload?fileNameV1040085-01.zip [rootnode5 ~]# ll total 22776 -rw-------. 1 root root 1066 Jan 21 14:59 anaconda-ks.cfg -r–r–r–. 1 root root 23…

【Linux】进程优先级以及Linux内核进程调度队列的简要介绍

进程优先级 基本概念查看系统进程修改进程的优先级Linux2.6内核进程调度队列的简要介绍和进程优先级有关的概念进程切换 基本概念 为什么会存在进程优先级&#xff1f;   进程优先级用于确定在资源竞争的情况下&#xff0c;哪个进程将被操作系统调度为下一个运行的进程。进程…

Java的编程之旅31——多态

1.多态的简介 在Java中&#xff0c;多态是指通过父类的引用调用子类的对象&#xff0c;实现不同类型的对象的方法调用。 Java中的多态通过继承和重写实现。当子类继承父类并重写父类的方法时&#xff0c;可以通过父类的引用调用子类的对象&#xff0c;并且根据运行时实际的类型…