• GenericServlet、ServletConfig和ServletContext


    2023.10.19

    在编写一个Servlet类直接实现Servlet接口有什么缺点?

    • 我们只需要service方法,其他方法大部分情况下是不需要使用的,代码很臃肿。

            解决方法:编写一个GenericServlet类,这个类是一个抽象类,其中有一个抽象方法service。

    • GenericServlet实现Servlet接口。

    • GenericServlet是一个适配器。

    • 以后编写的所有Servlet类继承GenericServlet,重写service方法即可

    GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?

            由于service函数可能要使用到init函数中的ServletConfig对象, 所以需要定义一个ServletConfig成员变量用来接收init中的ServletConfig对象,然后在getServletConfig返回这个config对象,这样子service就可以获取这个config对象了。

            此时又有个问题:如果程序员将init方法重写了怎么办?那之前在init方法中对成员变量的赋值操作就都被覆盖了。  解决方法是:先将该init方法用final修饰起来,再定义一个成员方法init,这个方法专门供子类重写,然后在final修饰的init方法中调用该init:this.init();   

            改造后的GenericServlet类代码如下:

    1. package com.servlet;
    2. import jakarta.servlet.*;
    3. import java.io.IOException;
    4. /**
    5. * 编写一个标准通用的Servlet,起名:GenericServlet
    6. * 以后所有的Servlet类都不要直接实现Servlet接口了。
    7. * 以后所有的Servlet类都要继承GenericServlet类。
    8. * GenericServlet 就是一个适配器。
    9. */
    10. public abstract class GenericServlet implements Servlet {
    11. private ServletConfig config;
    12. @Override
    13. public final void init(ServletConfig Config) throws ServletException {
    14. this.config = Config;
    15. this.init();
    16. }
    17. //这个init方法是供子类重写的
    18. public void init(){
    19. }
    20. @Override
    21. public ServletConfig getServletConfig() {
    22. return config;
    23. }
    24. /**
    25. * 抽象方法,这个方法最常用。所以要求子类必须实现service方法。
    26. */
    27. public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;
    28. @Override
    29. public String getServletInfo() {
    30. return null;
    31. }
    32. @Override
    33. public void destroy() {
    34. }
    35. }

     ServletConfig

    • 什么是ServletConfig?

      • Servlet对象的配置信息对象。

      • ServletConfig对象中封装了标签中的配置信息。(web.xml文件中servlet的配置信息)

    • 一个Servlet对应一个ServletConfig对象。

    • Servlet对象是Tomcat服务器创建,并且ServletConfig对象也是Tomcat服务器创建。并且默认情况下,他们都是在用户发送第一次请求的时候创建。

    • Tomcat服务器调用Servlet对象的init方法的时候需要传一个ServletConfig对象的参数给init方法。

    • ServletConfig接口的实现类是Tomcat服务器给实现的。

    • ServletConfig接口有哪些常用的方法?

    1. public String getInitParameter(String name); // 通过初始化参数的name获取value
    2. public Enumeration getInitParameterNames(); // 获取所有的初始化参数的name
    3. public ServletContext getServletContext(); // 获取ServletContext对象
    4. public String getServletName(); // 获取Servlet的name

             以上方法在Servlet类当中,都可以使用this去调用。因为GenericServlet实现了ServletConfig接口。

    ServletContext

    • 一个Servlet对象对应一个ServletConfig。

    • 只要在同一个webapp当中,只要在同一个应用当中,所有的Servlet对象都是共享同一个ServletContext对象的。

    • ServletContext对象在服务器启动阶段创建,在服务器关闭的时候销毁。这就是ServletContext对象的生命周期。ServletContext对象是应用级对象。

    • Tomcat服务器中有一个webapps,这个webapps下可以存放多个webapp,假设有100个webapp,那么就有100个ServletContext对象。但是,一个webapp肯定是只有一个ServletContext对象。

    • ServletContext被称为Servlet上下文对象。(Servlet对象的四周环境对象。)

    • 一个ServletContext对象通常对应的是一个web.xml文件。

    • ServletContext是一个接口,Tomcat服务器对ServletContext接口进行了实现。

      • ServletContext对象的创建也是Tomcat服务器来完成的。启动webapp的时候创建的。

    ServletContext接口中有哪些常用的方法?

    1. public String getInitParameter(String name); // 通过初始化参数的name获取value
    2. public Enumeration getInitParameterNames(); // 获取所有的初始化参数的name
    1. pageSize
    2. 10
    3. startIndex
    4. 0
    1. // 获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态获取应用的根路径
    2. // 在java源码当中,不要将应用的根路径写死,因为你永远都不知道这个应用在最终部署的时候,起一个什么名字。
    3. public String getContextPath();
    4. //String contextPath = application.getContextPath();
    1. // 获取文件的绝对路径(真实路径)
    2. public String getRealPath(String path);
    1. // 通过ServletContext对象也是可以记录日志的
    2. public void log(String message);
    3. public void log(String message, Throwable t);
    4. // 这些日志信息记录到哪里了?
    5. // localhost.2021-11-05.log
    6. // Tomcat服务器的logs目录下都有哪些日志文件?
    7. //catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
    8. //localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
    9. //localhost_access_log.2021-11-05.txt 访问日志

            ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)

            如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中。

            为什么是所有用户共享的数据? 不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义。

            为什么数据量要小? 因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去。

            为什么这些共享数据很少的修改,或者说几乎不修改?所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的。

            数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率。

    1. // 存(怎么向ServletContext应用域中存数据)
    2. public void setAttribute(String name, Object value); // map.put(k, v)
    3. // 取(怎么从ServletContext应用域中取数据)
    4. public Object getAttribute(String name); // Object v = map.get(k)
    5. // 删(怎么删除ServletContext应用域中的数据)
    6. public void removeAttribute(String name); // map.remove(k)

             ps:以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范当中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的。)使用HttpServlet处理HTTP协议更便捷。它的继承结构:

    1. jakarta.servlet.Servlet(接口)【爷爷】
    2. jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
    3. jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
    4. 我们以后编写的Servlet要继承HttpServlet类。

     

     

     

  • 相关阅读:
    Python中的函数
    【树莓派】常规操作8则
    java递归获取所有的子级节点
    风格迁移常用代码
    如何利用 promise 影响代码的执行顺序?
    STM32项目 -- 选题分享(部分)
    IDEA快捷键第二版
    sealos一键部署K8S环境(sealos3.0时代教程过时了,目前已经4.0了,请移步使用Sealos一键安装K8S)
    Go 消息队列及工作池处理
    Linux 文件基础
  • 原文地址:https://blog.csdn.net/m0_61028090/article/details/133925675