0
点赞
收藏
分享

微信扫一扫

Java基础 - servlet(一)

楚木巽 2022-04-13 阅读 42
java

1、servlet应用实例

1、客户端提交一个表单数据访问服务端的整个流程图:

​ 客户端访问add.html页面并通过Http Request携带表单请求数据,通过web.xml中的映射关系,找到AddServlet类中的doPost方法,并执行该方法。
在这里插入图片描述

2、编写一个add.html表单提交页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="add" method="post">
        名称: <input type="text" name="fname"/></br/>
        价格: <input type="text" name="price"/></br/>
        库存: <input type="text" name="fcount"/></br/>
        备注: <input type="text" name="remark"/></br/>
        <input type="submit" value="添加" />
    </form>
</body>
</html>

3、提交表单到AddServlet类,问题,add方法是如何映射到AddServlet类中的方法的?

  • 在web.xml中配置映射关系

​ 1、用户发送请求,action=add
​ 2、项目中,web.xml中找到url-pattern = /add ->第12行
​ 3、找到第11行的servlet-name = AddServlet
​ 4、找和servlet-mapping中servlet-name一致的servlet,找到第8行
​ 5、再找到第9行servlet-class = com.atguigu.servlets.AddServlet
​ 6、用户发送的是post请求(method=post),因此tomcat会执行AddServlet类中的doPost方法

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>AddServlet</servlet-name>
        <servlet-class>com.atguigu.servlets.AddServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>AddServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>

    <!--
        1、用户发送请求,action=add
        2、项目中,web.xml中找到url-pattern = /add  ->第12行
        3、找到第11行的servlet-name = AddServlet
        4、找和servlet-mapping中servlet-name一致的servlet,找到第8行
        5、再找到第9行servlet-class = com.atguigu.servlets.AddServlet
        6、用户发送的是post请求(method=post),因此tomcat会执行AddServlet类中的doPost方法
    -->
</web-app>

4、AddServlet类

  • 该类需要继承javax.servlet.http中的HttpServlet类
  • 由于表单提交时选择的是post方式请求,所有就会调用该类的doPost方法
  • HttpServletRequest request对象就可以获取到表单提交的数据
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 AddServlet extends HttpServlet {

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fname = request.getParameter("fname");
        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        String fcountStr = request.getParameter("fcount");
        int fcount = Integer.parseInt(fcountStr);
        String remark = request.getParameter("remark");
        System.out.println("fname:" + fname);
        System.out.println("price:" + price);
        System.out.println("fcount:" + fcount);
        System.out.println("remark:" + remark);

    }

}

页面:
在这里插入图片描述

打印结果:

fname:apple
price:5
fcount:30
remark:ok

2、servlet中处理中文乱码问题

  • post请求
    设置字符编码。防止中文乱码,需要注意的是,设置编码这一句代码必须在所有的获取参数动作之前

    request.setCharacterEncoding("UTF-8");
    String fname = request.getParameter("fname");
    
  • get请求

    在tomcat8之后不需要处理。

3、servlet的继承关系

3.1 继承关系

javax.servlet.Servlet接口
	javax.servlet.GenericServlet抽象类
		javax.servlet.http.HttpServlet抽象子类

1、javax.servlet.http.HttpServlet抽象子类:

  • void service(request.response) --不是抽象的

    • 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);
              }
      
          }
      
    • 各种if判断,根据请求方式不同,决定去调用不同的do方法

    • 在HttpServlet这个抽象类中,do方法都差不多

      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              String msg = lStrings.getString("http.method_post_not_supported");
              this.sendMethodNotAllowed(req, resp, msg);
          }
      
      
      private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
              String protocol = req.getProtocol(); // 获取协议
              if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) {
                  resp.sendError(405, msg);
              } else {
                  resp.sendError(400, msg);
              }
      
          }
      

    2、 浏览器页面出现405的原因

    ​ 1)在AddServlet类中没有doGet()方法

    ​ 2)页面模拟发送get请求,断点调试

    由于是get请求,所有会执行HttpServlet类中service()方法中的doGet()方法
    在这里插入图片描述

    进入到doGet()方法
    在这里插入图片描述

    sendMethodNotAllowed()方法
    在这里插入图片描述

    页面显示
    在这里插入图片描述

3、小结

​ 1)继承关系:HttpServlet -> GenericServlet -> Servlet

​ 2)Servlet中的核心方法:init(),service(),destroy()

​ 3)服务方法:当有请求过来时,service方法会在那个响应(其实是tomcat容器调用的)

​ 在HttpServlet中我们会去分析请求的方式:到底是get、post、head、delete方法等等

​ 然后再决定调用的是哪个do开头的方法

​ 那么在HttpServlet中这些do方法默认都是405实现风格 - 要我们子类去实现对应的方法,否则默认会把405错误。

​ 4)因此,在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法。

4、Servlet的生命周期

1)生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service() ,destroy()方法

2)默认情况下:

  • 第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init()方法)、然后服务(调用service()方法)
  • 从第二次请求开始。每一次都是调用服务方法
  • 当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法

3)通过案例发现:

  • Servlet实例tomcat只会创建一个实例,所有的请求都是这一个实例去响应。
  • 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务,这样的好处提高服务的启动速度;缺点是第一次请求时耗时较长。
  • 因此得出结论:如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet启动时机。

4)Servlet的初始化时机

  • 默认是第一次接收请求时,实例化,初始化

        <servlet>
            <servlet-name>DemoServlet</servlet-name>
            <servlet-class>com.atguigu.servlets.DemoServlet</servlet-class>
            <!-- servlet启动的优先级,load-on-startup的值越小优先级越高 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    

5)Servlet在容器中是:单例的、线程不安全的

  • 单例:所有的请求都是同一个实例去响应
  • 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另外一个线程改变了这个值,从而导致这个线程的执行逻辑发生改变。
  • 因为servlet是线程不安全的,尽量不要在servlet中定义成员变量。如果不得不定义成员变量的值,① 那么不要去根据成员变量的值做一些逻辑判断。

5、HTTP协议

​ 1)Http称之为 超文本传输协议

​ 2)HTTP是无状态的

​ 3)HTTP包含两部分:请求和响应

  • 请求:

    请求包含三个部分:1.请求行;2.请求消息头;3.请求主体

    ​ 1)请求行包含三个信息:1.请求的方式;2.请求的URL;3.请求的协议(一般都是HTTP1.1)

    ​ 2)请求消息头包含很多客户端浏览器需要告诉服务器的信息。比如:浏览器的型号、版本…

    ​ 3)请求主体,三种情况

    ​ get方式,没有请求体,但是有一个queryString

    ​ post方式,有请求体,form data

    ​ json格式,有请求体,request payload

  • 响应

    响应也包含三部分:1.响应行;2.响应头;3.响应体

    1)响应行包含三个信息:1.协议 ;2.响应状态码(200);3.响应状态(ok)

    2)响应头:包含服务器的信息;服务器发送给浏览器的信息

    3)响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是 …)

6、会话机制

6.1 案例分析

1、servlet类

public class Demo02Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        System.out.println("session ID:" + session.getId());
    }
}

2、web.xml

    <servlet>
        <servlet-name>Demo02Servlet</servlet-name>
        <servlet-class>com.atguigu.servlets.Demo02Servlet</servlet-class>
        <!-- servlet启动的优先级 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo02Servlet</servlet-name>
        <url-pattern>/demo02</url-pattern>
    </servlet-mapping>

3、浏览器请求,服务端生成sessionID,第一次会放到响应头里面返回给客户端浏览器
在这里插入图片描述

浏览器响应头中的JSSIONID就是服务器生成的并返回给浏览器的
在这里插入图片描述

浏览器第二次请求时,打印断点查看session信息,发现session信息不是新的,还是第一次创建时的那个session
在这里插入图片描述

第二次查看浏览器时发现在响应头里面就没有了JSESSIONID,此时的JSESSIONID放到了请求头的Cookie里面啦。
在这里插入图片描述

6.2 会话跟踪技术

1)客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端浏览器。下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到,服务器就可以判断这一次请求和上一次请求是同一个客户端,从而能够区分开。

6.3 session保存作用域

  • session保存作用域和具体的某一个session对应的

  • 常见的API

    void session.setAttribute(k,v);
    Object session.getAttribute(k)
    

在这里插入图片描述

1、demo03Servlet:向session中设置值

public class Demo03Selrvlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getSession().setAttribute("uname","zhangsan");
    }
}

2、demo04Servlet:从session中获取值

public class Demo04Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object uname = request.getSession().getAttribute("uname");
        System.out.println(uname);
    }
}

3、第一次通过edgn浏览器请求demo03,向HttpSession里面设置值,再使用同样的浏览器访问demo04从HttpSession中获取值,发现可以获取到值。

在这里插入图片描述

4、再换用chrome浏览器请求demo04,发现没有获取到值。
在这里插入图片描述

7、服务器内部转发以及客户端重定向

7.1 服务器内部转发

一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的。
在这里插入图片描述

客户端请求demo06,执行以上代码,程序将会转发到demo07中执行代码,并由demo07返回结果给客户端。

代码演示:

Demo05Servlet:

public class Demo05Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo05.....");
        // 服务器端内部转发
        request.getRequestDispatcher("demo06").forward(request,response);
    }
}

Demo06Servlet:

public class Demo06Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo06......");
    }
}

客户端请求访问demo05,服务端首先请求demo05,在转发到demo06,客户端地址栏URL不会改变,整个过程客户端是不知道的

打印结果:
在这里插入图片描述

7.2 客户端重定向

两次请求响应的过程,客户端知道请求URL有变化。
在这里插入图片描述

代码:

public class Demo05Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo05.....");
        // 演示客户端重定向
        response.sendRedirect("demo06");
    }
}
public class Demo06Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo06......");

    }
}

客户端请求demo05,发现地址栏的URL变为demo06了。
在这里插入图片描述

举报

相关推荐

0 条评论