第七章Servlet

什么是Servlet

为什么需要Servlet

Servlet名字的含义

  • Servlet=Server+applet
  • Server:服务器
  • applet:小程序
  • Servlet含义是服务器端的小程序

我们需要将一次HTTP请求-响应(不限于HTTP),对应到Java的语句(Java的语句一定放在方法中)——将请求-响应的处理过程对应到某个类下的方法对应起来——Servelet容器的功能

生活中的例子

./images

对应的web应用

./images

  • 在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的**『控制器』**

  • Servlet是Java提供的一门动态web资源开发的技术,如何称为动态,也就是不同的用户访问一个资源,所展现的页面是不一样的,比如张三访问是张三欢迎你,李四访问是李四欢迎你,我访问是你不能访问该页面,类似这种功能的实现

  • Servlet是JavaEE的规范之一,其实就是一个接口,将来我们需要具有Servlet功能的类型来实现Servlet接口,并由web服务器来运行Servlet

  • Tomcat是一个轻量级的web服务器,可以称为web容器,Servlet容器,web服务器封装了http协议,简化我们的开发,其次将web项目部署到服务器中,可以对外提供网上浏览服务

img

从不同角度来看Servlet

img

总过程

img

Servlet之间的继承关系

Servlet的继承关系 - 重点查看的是服务方法(service())

image-20221211145758047

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

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();
}
  • 这是Servlet的源码,我们看到有

    • void init(config) - 初始化方法

    • void service(request,response) - 服务方法

    • ​ void destory() - 销毁方法

GenericServlet抽象方法

  public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
  • void service(request,response) - 我们仍然是抽象的

HttpServlet 抽象子类

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }

        this.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);
        }

    }
  • void service(HttpServletRequest req, HttpServletResponse resp) - 不是抽象的
  • String method = req.getMethod(); 这个方法是获取请求的方式
    • 然后通过后面的if和else if的结构来通过什么类型的方法去执行不同的逻辑
      • 决定调用的是哪个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这个抽象类中,do方法都差不多
  • 那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误

小结

  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方法

Servlet生命周期

从Servlet接口方法开始

image-20221211172758372

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

例子

//演示Servlet的生命周期
public class Demo2 extends HttpServlet {
    public Demo2(){
        System.out.println("正在实例化....");
    }

    @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("正在销毁......");
    }
}

执行结果

//第一次访问Demo2
正在实例化....
正在初始化.....
正在服务.....
//第二次访问Demo2
正在服务.....
//第三次访问Demo3
正在服务.....

结论

默认情况下:

  • 第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())

  • 从第二次请求开始,每一次都是服务

  • 当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法 destroy()

  • 从调用构造方法可以看出 Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。

    • 一般情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务.这样的好处是什么? 提高系统的启动速度 。 这样的缺点是什么? 第一次请求时,耗时较长。

      • 因此得出结论: 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机。

Servlet在容器中是:单例的、线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
- 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:
- ①不要去修改成员变量的值
- ②不要去根据成员变量的值做一些逻辑判断

修改Servlet创建对象的时机

<!-- 配置Servlet本身 -->
<servlet>
    <!-- 全类名太长,给Servlet设置一个简短名称 -->
    <servlet-name>HelloServlet</servlet-name>

    <!-- 配置Servlet的全类名 -->
    <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>

    <!-- 配置Servlet启动顺序 -->
    <load-on-startup>1</load-on-startup>
</servlet>
  • 我们可以通过来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0,
  • 修改启动顺序后:创建就在Web应用启动过程中

小结

名称时机次数
创建对象默认情况:接收到第一次请求 修改启动顺序后:Web应用启动过程中一次
初始化操作创建对象之后一次
处理请求接收到请求多次
销毁操作Web应用卸载之前一次

Servlet容器

容器

在开发使用的各种技术中,经常会有很多对象会放在容器中。

容器提供的功能

容器会管理内部对象的整个生命周期。对象在容器中才能够正常的工作,得到来自容器的全方位的支持。

  • 创建对象
  • 初始化
  • 工作
  • 清理

容器本身也是对象

  • 特点1:往往是非常大的对象
  • 特点2:通常的单例的

典型Servlet容器产品举例

  • Tomcat
  • jetty
  • jboss
  • Weblogic
  • WebSphere
  • glassfish

Servlet相关的保存作用域

  • 原始情况下,保存作用域我们可以认为有四个: page(页面级别,现在几乎不用) , request(一次请求响应范围) , session(一次会话范围) ,application(整个应用程序范围)

request:一次请求响应范围

@WebServlet("/demo1")
public class Demo1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1向request保存作用域保存数据
        request.setAttribute("uname","lili");
        //向demo2进行客户端重定向
        response.sendRedirect("demo2");
        //向demo2进行服务器端转发
        request.getRequestDispatcher("demo2").forward(request,response);
    }
}
@WebServlet("/demo2")
public class Demo2  extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object uname = request.getAttribute("uname");
        System.out.println("uname="+uname);
    }
}
  • 根据打印结果 :
    • 进行客户端重定向 我们的Demo2输出null,说明获取不到name这个属性的数据
    • 进行服务器转发 我们的Demo2输出了lsc,说明获得到了name这个属性的数据

image-20221213133006129

session:一次会话范围有效

@WebServlet("/demo3")
public class Demo3 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session=request.getSession();
        session.setAttribute("uname","lili");
        //向demo2进行客户端重定向
//        response.sendRedirect("demo2");
        //3.服务器端转发
        request.getRequestDispatcher("demo2").forward(request,response);
    }
@WebServlet("dem04")
public class Demo4 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        System.out.println("uname"+session.getAttribute("uname"));
    }
}

根据打印结果 :

  • 我们使用同一个客户端(打开浏览器不关闭),不管是客户端重定向还是服务器转发都能获取到name的属性值
  • 如果使用的不是同一个客户端,不管是客户端重定向还是服务器转发都获取不到name的属性值

image-20221213141919228

application: 一次应用程序范围有效

@WebServlet("/demo5")
public class Demo5 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.向application保存作用域保存数据
        //ServletContext : Servlet上下文
        ServletContext application = request.getServletContext();
        application.setAttribute("uname","lili");
        //2.客户端重定向
        response.sendRedirect("demo06");

        //3.服务器端转发
        //request.getRequestDispatcher("demo04").forward(request,response);
    }
}
@WebServlet("/demo6")
public class Demo6 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取application保存作用域保存的数据,key为uname
        ServletContext application = request.getServletContext();
        Object unameObj = application.getAttribute("uname");
        System.out.println("unameObj = " + unameObj);
    }
}
  • 不管是不是同一个客户端,不管是转发还是重定向都能获取到数据

image-20221213142758379

ServletContext : Servlet上下文

  • 什么叫上下文:

    • 拿生活中的例子,比如张三和李四在聊天,聊一个关于小明的八卦,他两已经聊了一会,突然王五来了说你们在聊啥,我想一起聊,王五想聊,就必须要知道张三和李四之前聊天的内容,才能加入他们一起聊小明的八卦,王五听张三和李四之前聊天的内容就是所谓的上下文
    • 我们的Servlet容器是Tomcat,我们可以不严谨的称为Servlet应用程序(因为有大量的Servlet),也就是我们的Servlet上下文,我们TomCat这次启动,也就是上下文开始
  • 代表:整个Web应用

  • 是否单例:是

  • xxxxxxxxxx1 1request.getContextPath()java

    • 获取某个资源的真实路径:getRealPath()
    • 获取整个Web应用级别的初始化参数:getInitParameter()
    • 作为Web应用范围的域对象
      • 存入数据:setAttribute()
      • 取出数据:getAttribute()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库里不会投三分

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值