Tomcat和Servlet学习记录

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
    }
}
  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值