GenericServlet原理:Servlet;ServletConfig;ServletContext

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

    • service方法是最常用的,其他方法大部分情况下是不需要使用的。如此其他的方法就显得有些冗余,代码很丑陋。
    • 解决思路:编写一个“中间商”,使中间商实现Servlet接口,我们编写的Servlet类再继承“中间商”

Servlet

  • 版本一
    • 创建一个抽象类GenericServlet实现Servlet接口,将子类需要的方法设置为抽象方法。
    • 将子类继承 GenericServletTest抽象类,并实现对应的抽象方法。
    • 需要注意的是:父类的init方法(非抽象)也会执行
      在这里插入图片描述
  • 版本一问题:子类继承GenericServletTest,并实现他的抽象方法,父类的init方法也会实现。会执行父类GenericServlet类中的init方法。
    子类的对象是Tomcat创建的,init方法也是Tomcat调用的,当然,init方法中需要传入的ServletConfig类型的参数也是tomcat创建的。这里需要考虑到ServletConfig对象肯定在service方法中使用
  • 解决方法为了确保ServletConfig可以在其他方法中使用,把ServletConfig设置为成员变量

  • 版本二
    • 设置成员变量servletConfig
    • 考虑到该变量是Tomcat创建的,成员变量的servletConfig一开始是null,如何赋值呢?
    • 下图是解决方法:
      在这里插入图片描述- 版本二问题:通过上述方法可以解决其他方法获得servletConfig问题,但是如果子类想重写init方法时,父类的init方法可能被破坏,导致servletConfig获取不到,成员变量依旧为空。
  • 解决方法:使父类的init方法被final修饰,即父类的init方法不能被重写,杜绝了重写init方法导致成员变量为空的问题
  • 解决后问题:万一子类实在是需要重写init方法怎么办?
  • 解决方法:写一个无参的init类,使子类重写无参的init类,方法重载

  • 版本三
    在这里插入图片描述

  • “中间商代码”
package adapter;
import jakarta.servlet.*;
import java.io.IOException;

/**    理解 GenericServlet 原理
 *  设置“中间商”——GenericServletTest
 *      在servlet包中的loginServlet类继承Servlet类之后需要实现Servlet的所有方法,但是在使用时我们发现,不是所有方法都需要
 *实现,那么loginServlet类中的有些方法就显得冗余。
 *      此时我们创建一个抽象类GenericServlet实现Servlet接口,将需要编写的方法设置为抽象方法
 *      将loginServlet类继承 GenericServletTest抽象类,并实现对应的抽象方法。
 */
public abstract class GenericServletTest implements Servlet {
    private ServletConfig servletConfig;
    /**
     *      子类loginServlet继承GenericServletTest,并实现他的抽象方法,父类的init方法也会实现。会执行父类GenericServlet类中的init方法。
     *      loginServlet的对象是Tomcat创建的,init方法也是Tomcat调用的,当然,init方法中需要传入的ServletConfig类型的参数也是tomcat
     *创建的。
     *      这里需要考虑到ServletConfig对象肯定在service方法中使用,为了确保ServletConfig可以在其他方法中使用,把ServletConfig设置为成员变量
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public final void init(ServletConfig servletConfig) throws ServletException {
        //Tomcat创建的servletConfig参数,传入之后,通过等号赋给成员变量servletConfig。
        this.servletConfig =servletConfig;
        /**
         *当我们确定了“中间商”之后,有的需求,子类还需要重写init方法
         *  在子类重写init方法时,父类的init方法可能被破坏
         *     因为父类中的init方法可以从Tomcat创建的参数中获得servletConfig参数,进一步赋值给成员变量
         *     如果被破坏,可能成员变量servletConfig类型的变量为空,那么在其他的方法中就不能使用。
         *     解决:父类的init方法不让子类重写,避免错误,在父类的init方法上添加final关键字
         *  但,当子类确实需要重写init方法时,可以编写另外一个init方法——方法重载
         */
        this.init();
    }

    /**
     * 该方法供子类重写
     */
    public void init(){

    }

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    /**
     * 抽象方法
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse)
            throws ServletException, IOException ;



    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

  • 子类代码
package adapter;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import java.io.IOException;

public class loginServlet extends GenericServletTest{

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("");
        //在loginServlet子类中使用servletConfig对象
        ServletConfig config = this.getServletConfig();
    }
}

  • GenericServlet源代码:public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
  • 到此为止,明确了GenericServlet为什么实现Servlet

ServletConfig

  • 上文提到,需要考虑ServletConfig对象肯定在service方法中使用,那ServletConfig是什么呢?
  • jakarta.servlet.ServletConfig,ServletConfig是Servlet规范中的一员。
  • ServletConfig是一个接口
    • 实现ServletConfig接口的是Tomcat服务器,且一个servlet对象就有一个对应的ServletConfig对象不同的servlet有不同的ServletConfig对象
    • 创建ServletConfig对象的是Tomcat服务器
  • ServletConfig的用处
    • ServletConfig对象是servlet对象的配置信息对象
    • ServletConfig对象包含的信息:web.xml文件中标签的配置信息
    • Tomcat将web.xml文件配置信息自动包装到ServletConfig对象中。
      • 这就解释了:ServletConfig方法.getServletName();
        在这里插入图片描述在这里插入图片描述

  • 思考:既然web.xml文件中标签的配置信息都可以被包装到 ServletConfig对象里面,那么是不是除了servlet-name,我们还可以往里面塞点其他的东西,取的时候也可以通过ServletConfig获得。

  • 答案是可以的,结合ServletConfig的四个方法,其中一个是getInitParameterNames(),就可以知道塞了什么——init-param初始化参数
    在这里插入图片描述

  • java.util.Enumeration<java.lang.String> getInitParameterNames()
    java.lang.String getInitParameter(java.lang.String name)
    上面的两个方法可以获取到初始化参数

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">
    <servlet>
        <servlet-name>config</servlet-name>
        <servlet-class>javaWeb.servlet.configTest.ConfigTestServlet</servlet-class>
        <!--配置Servlet对象初始化信息-->
        <init-param>
            <param-name>driver</param-name>
            <param-value>com.mysql.cj.jdbc.Driver</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/yiyu</param-value>
        </init-param>
        <init-param>
            <param-name>user</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123123</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>config</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>
</web-app>
public class ConfigTestServlet extends GenericServlet {
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        //获取ServletConfig对象
        ServletConfig config = this.getServletConfig();
        //输出对象
        PrintWriter out=response.getWriter();
        out.print("ServletConfig对象——"+config);

        out.print("<br>"+"<servlet-name>"+config.getServletName()+"<servlet-name>");
        // 通过ServletConfig对象方法,获取<init-param></init-param>初始化参数
        Enumeration<String> initParameterNames= config.getInitParameterNames();
        while(initParameterNames.hasMoreElements()){
            String name = initParameterNames.nextElement();
            //config.getInitParameter("")  通过名字获得vlaue
            String diver = config.getInitParameter(name);
            out.print("<br>"+name+"="+diver);
        }
    }
}

  • GenericServlet源代码:public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
  • 到此为止,就可以知道ServletConfig是什么了,也知道GenericServlet继承了ServletConfig,因为我们写的Servlet类需要继承GenericServlet,也就是说ServletConfig的方法直接使用this调用就可以。

ServletConfig ——ServletContext

  • 上文说到ServletConfig有四个方法,到目前为止我们知道三个是用于获得的初始化内容的。那么另外一个是什么呢?——ServletContext在这里插入图片描述
  • ServletContext是一个接口
  • ServletContext的实现,创建都是Tmocat服务器。
  • ServletContext在服务器启动时创建,在服务器关闭时销毁
  • ServletContext是Servlet的环境对象,背景;应用域
    • 只要在同一个webapp当中,所有的servlet共享放在ServletContext对象当中的数据
      • 50个学生在教室里面听老师讲课
      • 50个servlet 一个ServletContext老师
  • Tomcat当中有webapps,这个webapps里面可以存放多个webapp,有多少个webapp就有多少个ServletContext。即:ServletContext是应用级对象
    在这里插入图片描述

  • 既然ServletContext是应用级的对象,那么我们对多个servlet对象进行同样的初始化时,有些数据就可以塞到ServletContext里面
    在这里插入图片描述
<!--Context初始化参数  以下的配置信息可以使用ServletContext对象获得-->
    <context-param>
        <param-name>pageSize</param-name>
        <param-value>10</param-value>
    </context-param>
    <context-param>
        <param-name>startIndex</param-name>
        <param-value>0</param-value>
    </context-param>

  • 以上,我们分辨出来了 ServletConfig ——ServletContext两者的区别。那么如何判断数据是否应该存放在ServletContext中?
    • 如果所有用户共享同一份数据,且数据量小,且该数据很少被修改,就可以放入
      • 数据量小:数据量大的话太占用堆内存,并且该context对象生命周期长,服务器关闭的时候,该对象才会被销毁,大量数据会影响服务器性能
      • 很少修改:如果涉及修改操作,必然存在线程并发所带来的安全问题,所以ServletContext对象中的数据一般都是可读的
      • 向ServletContext应用域中存储数据,也等于是将数据存放到缓存cache当中了
  • 判断出应该放入ServletContext的数据,如何放、如何取、如何删
    • 存数据
      public void setAttribute(String name,Object value)
    • 取数据
      public void getAttribute(String name);
    • 删数据
      public void removeAttribute(String name);
 缓存机制
 - 堆内存当中的字符串常量池。
    - "abc" 先在字符串常量池中查找,如果有,直接拿来用。如果没有则新建,然后再放入字符串常量池。
  - 堆内存当中的整数型常量池。
    - [-128 ~ 127] 一共256个Integer类型的引用,放在整数型常量池中。没有超出这个范围的话,直接从常量池中取。
  - 连接池(Connection Cache)
    - 这里所说的连接池中的连接是java语言连接数据库的连接对象:java.sql.Connection对象。
    - JVM是一个进程。MySQL数据库是一个进程。进程和进程之间建立连接,打开通道是很费劲的。是很耗费资源的。怎么办?可以提前先创建好N个Connection连接对象,将连接对象放到一个集合当中,我们把这个放有Connection对象的集合称为连接池。每一次用户连接的时候不需要再新建连接对象,省去了新建的环节,直接从连接池中获取连接对象,大大提升访问效率。
    - 连接池
      - 最小连接数
      - 最大连接数
      - 连接池可以提高用户的访问效率。当然也可以保证数据库的安全性。
  - 线程池
    - Tomcat服务器本身就是支持多线程的。
    - Tomcat服务器是在用户发送一次请求,就新建一个Thread线程对象吗?
      - 当然不是,实际上是在Tomcat服务器启动的时候,会先创建好N多个线程Thread对象,然后将线程对象放到集合当中,称为线程池。用户发送请求过来之后,需要有一个对应的线程来处理这个请求,这个时候线程对象就会直接从线程池中拿,效率比较高。
      - 所有的WEB服务器,或者应用服务器,都是支持多线程的,都有线程池机制。
  - redis
    - NoSQL数据库。非关系型数据库。缓存数据库。
  - 向ServletContext应用域中存储数据,也等于是将数据存放到缓存cache当中了。
  
  ********动力节点*************

  • ServletContext的方法
    在这里插入图片描述

    • 动态获取根路径 getContextPath();该处获得的根,是index比web.cml程序多写的那个应用的根。

    • 获取文件的绝对路径 getRealPath();

      • ServletContext对象的getRealPath(“index.html”);
      • 输出:E:\IDEAProjects\javaweb\out\artifacts\servlet03_war_exploded\index.html
    • 记录日志 log(String message) / log(String message,Throwable t)

      • ServletContext对象.log(“哒哒哒”);
      • 在控制台查看路径:Using CATALINA_BASE
      • 日志类型在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值