servlet学习笔记

Servlet学习

系统架构

1.系统架构包括:C/S(Client/Server)架构、B/S(Browser/Server)架构

B/S结构通信原理

  • 关于域名

    • https://www.baidu.com (http:110.242.68.3:80/index.html)
  • IP地址

    • 具有唯一性
    • 不同设备通信需要通过ip地址才能建立连接
  • 端口号

    • 同一台设备,端口号唯一
    • 一个端口代表一个软件
    • 一个计算机当中有很多软件,每一个软件启动之后都有一个端口号
  • WEB系统的通信原理:

    • 通信步骤:
      • 第一步:用户输入url
      • 第二步:域名解析器进行域名解析:http:110.242.68.3:80/index.html
      • 第三步:浏览器软件在网络中搜索110.242.68.3这一台主机,知道找到这台主机。
      • 第四步:定位110.242.68.3这台主机上的服务器软件,80端口可以对应到定位的服务器软件。
      • 第五步:80端口对应的服务器软件得知浏览器想要的资源名为:index.html
      • 第六步:服务器软件找到index.html文件,并将其内容直接输出响应到浏览器上。
      • 第七步:浏览器接受到来自服务器的代码(HTML CSS JS)
      • 第八步:浏览器渲染,执行HTML CSS JS代码,展示效果。
  • 什么是URL

    • 统一资源定位符(http://www.baidu.com)

关于web服务器

  • WEB服务器软件(提前开发好的)

    • Tomcat(WEB服务器)
    • jetty(WEB服务器)
    • JBOSS(应用服务器)
    • WebLogic(应用服务器)
    • WebSphere(应用服务器)
  • 应用服务器和WEB服务器的关系?

    • 应用服务器实现了javaEE的所有规范。(JavaEE有13种不同的规范)

    • WEB服务器只实现了JavaEE中的Servlet+JSP两个核心的规范

    • 通过这个讲解说明了:应用服务器包含了WEB服务器

    • JBOSS服务器中内嵌了Tomecat服务器

实现一个最基本的web应用(这个web应用中没有java小程序)

  • 第一步:找到CATALINA_HOME\webapps目录

    • 因为所有的webapp要放到webapps目录下。(Tomcat服务器的要求)
  • 第二步:在CATALINA_HOME\webapps目录下新建一个子目录,起名:oa

    • 这个目录就是webapp的名字
  • 第三步:在oa目录下新建资源文件。如index.html

    • 编写index.html文件的内容
  • 第四步:启动Tomcat服务器

  • 第五步:打开浏览器,输入url:

    • http://127.0.0.1:8080/oa/index.html
  • 浏览器输入URL与超链接效果一致

<!--注意一下路径,以/开始,带项目名,是一个绝对路径。不需要添加:http://127.0.0.1:8080-->
<a href="/oa/login.html">user.login</a>
<!--多个层级也没关系,正常访问即可-->
<!--注意:前段路径以"/"开始的,都是加项目名的-->
<a href="/oa/test/debug/d.html">d page</a>

对于一个动态的web应用来说,一个请求和相应的过程应该有多少个角色参与,角色和角色之间有多少协议?

  • 有哪些角色(在整个BS结构的系统中,有那些人参与进去了)

    • 浏览器软件的开发团队
    • WEB Server的开发团队(Tomcat、Jetty、WebLogic、JBOSS、WebSphere…)
    • DB Server的开发团队(Oracle、MySQL…)
    • webapp的开发团队(JavaWEB程序员开发)
  • 角色和角色之间需要遵循的规范和协议

    • webapp的开发团队 和 WEB Server的开发团队 之间有一套规范:JavaEE规范之一Servlet规范

      • Servlet规范的作用:
        • WEB Server 和 webapp解耦合
    • Browser 和 WebServer 之间一套传输协议:HTTP协议(超文本传输协议)

    • webapp开发团队 和 DB Server的开发团队 之间有一套规范:JDBC规范

  • Servlet规范:

    • 遵循Servlet规范的webapp,这个webapp就可以在不同的WEB服务器中运行。
    • Servlet规范包括:
      • 规范了哪些接口;
      • 规范了哪些类;
      • 规范了一个web应用中应该有哪些配置文件;
      • 规范了一个web应用中配置文件的名字;
      • 规范了一个web应用中配置文件存放的路径;
      • 规范了一个web应用中配置文件的内容;
      • 规范了一个合法有效的web应用的目录结构;

开发一个带有Servlet的webapp(重点)

  • 开发步骤:

    • 第一步:在webapp目录下新建目录,该目录即是webapp的名字
  • 该目录就是这个webapp的根

    • 第二步:在webapp的根下新建一个目录:WEB-INF

    • 该目录名字有Servlet规范规定

  • 第三步:在WEB-INF目录下新建一个目录:classes

    • 注意:该目录用于存放Java程序编译之后的class文件(这里存放的是字节码文件)
    • 第四步:在WEB-INF目录下新建一个目录:lib

    • 该目录不是必须的,但如果一个webapp需要第三方的jar包的话,这个jar包要放到这个lib目录下

    • 第五步:在WEB-INF目录下新建一个文件:web.xml

      • 注意:这个文件是必须的,这个文件也必须放在这里,这个web.xml文件就是一个配置文件,这个配置文件描述了请求路径和Servlet类之间的对照关系
      • 这个文件最好拷贝,别手写
    • 第六步:编写一个Java程序,这个Java程序必须实现Servlet接口。

      • 这个Servlet接口不在JDK中,因为Servlet不是JavaSE了,Servlet属于JavaEE,是另一套库类
    • Servlet接口是Oracle提供的。(最原始是sun公司提供的)

      • Servlet接口是JavaEE规范中的一员
  • Tomcat服务器实现了Servlet规范,所以Tomcat服务器也需要使用Servlet接口,Tomcat服务器中应该有这个接口,Tomcat服务器的CATALINA_HOME\lib目录下有一个servlet-api.jar,解压后可以看到Servlet.class文件

  • 重点:从JakartaEE9开始,Servlet接口全名变为:jakarta.servlet.Servlet

  • 第七步:编译项目

    • 重点:如何通过编译?配置环境变量CLASSPATH

      CLASSPATH=.;自己的路径\apache-tomcat-版本号\lib\servlet-api.jar

    • 注:该配置与Tomcat服务器运行与否并无关系,只是为了让编写的项目能够正常编译生成class文件

    • 第八步:将编译后生成的class文件拷贝到WEB-INF\classes目录下

    • 第九步:在web.xml文件中编写配置信息,让请求路径和Servlet类名关联

      • 专业术语:在web.xml文件中注册Servlet类
  • 第十步:启动Tomcat服务器

    • 第十一步:打开浏览器,在浏览器地址栏上输入url

      • 浏览器的请求路径不能随便写,这个请求路径必须和web.xml文件中的url-pattern一致
      • 注意:浏览器上的请求路径和web.xml文件中的url-pattern的唯一区别就是:浏览器上的请求路径带项目名:/项目名
    • 浏览器上编写路径太复杂,可以使用超链接。(注:html页面只能放到WEB-INF目录外面)

    • main方法将由Tomcat服务器负责调用,Tomcat服务器启动的时候执行的就是main方法。我们只需要编写Servlet接口的实现类,然后将其注册到web.xml文件中。

解决Tomcat在DOS命令窗口中的乱码问题(控制台乱码)

将CATALINA_HOME/conf/logging.properties文件中的内容修改为:

java.util.logging.ConsoleHandler.encoding=GBK

向浏览器响应一段HTML代码

public void service(ServletRequest request, ServletResponse response){
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.print("<h1>hello servlet!</h1>");
}

在Servlet中连接数据库

  • Servlet是Java程序,所以在Servlet中完全可以编写JDBC代码连接数据库。
  • 在一个webapp中去连接数据库,需要将驱动jar包放到WEB-INF/lib目录下。(com.mysql.cj.jdbc.Driver这个类就在驱动jar包当中)

在集成开发环境当中开发Servlet程序

  • 集中开发工具很多,其中使用比较多的是:
    • InterlliJ IDEA(这个居多,IDEA在提示功能方面强于Eclipse,也就是说IDEA使用起来更智能,更好用。JetBrain公司开发的,收费的)
      • Eclipse(这个少一点,但目前还有人用,不过处于减少趋势,Eclipse是IBM团队开发的,Eclipse寓意“日食”。而太阳是SUN。IBM团队开发Eclipse的寓意是吞并SUN公司,但是2009年的时候SUN公司被Oracle公司并购了。IBM并没有成功并购SUN公司)

Servlet对象的生命周期

  • Servlet对象生命周期:

    • Servlet对象什么时候被创建
    • Servlet对象什么时候被销毁
    • Servlet对象被创建了几个
    • Servlet对象的生命周期表示:一个Servlet对象从出生到死亡,整个过程是怎样的
  • Servlet对象由谁维护?

    • Servlet对象的创建,对象上方法的调用,对象最终的销毁,Javaweb程序员无权干预
    • Servlet对象的生命周期由Tomcat服务器(WEB Server)全权负责
    • Tomcat服务器又称为:WEB容器(WEB Container)
    • WEB容器来管理Servlet对象的生命周期
  • 思考:我们new的Servlet对象受WEB容器的管理嘛?

    • 不受WEB容器管理
    • WEB容器创建的Servlet对象,这些Servlet对象都会被放到一个集合中(HashMao),只有放到这个HashMap集合中的Servlet才能被WEB容器管理,自己new的Servlet对象不会被WEB容器管理。(自己new的Servlet对象不在WEB容器当中)
    • web容器底层应该有一个HashMap这样的集合,这个集合当中存储了Servlet对象和请求路径之间的关系
  • 怎么让服务器启动的时候创建Servlet对象呢?

    • 在Servlet标签中添加子标签,在该子标签中填写整数,越小的整数,优先级越高

    • <servlet>
          <servlet-name>自己起的名字</servlet-name>
              <servlet-class>自己编写的Servlet地址</--Servlet地址></servlet-class>
      	<load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
          <servlet-name>同上servlet-name</servlet-name>
          <url-pattern>链接地址</url-pattern>
      </servlet-mapping>
      

补充:多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。[1]如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。(来自百度百科)

  • Servlet类的调用次数?

    • 构造方法只执行一次
    • init方法只执行一次
    • service方法:用户发送n次请求,则执行n次
    • destroy方法:只执行一次
  • 当Servlet类中编写了一个有参构造方法,没有手动编写无参构造方法:

    • 报错500
    • 500是一个HTTP协议的错误状态码
    • 500一般情况下是因为服务器端的java程序出现了异常(服务器端的错误都是500,服务器内部错误)
    • 如果没有无参构造方法,会导致出现500错误,无法实例化Servlet对象
    • 所以,在Servlet开发中,不建议程序员来定义构造方法,因为定义不当,一不小心就会导致无法实例化Servlet对象
  • 思考:Servlet的无参构造方法是在对象第一次创建时执行,并且只执行一次,init方法跟无参构造方法一样,那无参构造方法可以代替init方法吗?

    • 不能
    • Servlet规范中有要求,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易造成无参构造方法消失,这个操作会导致无法实例化Servlet对象,所以init方法是有存在的必要的。
  • init方法是由Tomcat服务器调用的

  • init方法中的ServletConfig对象是由Tomcat创建的,也是由Tomcat传过来的

ServletConfig

  • 什么是ServletConfig
    • Servlet对象的配置信息对象
    • ServletConfig对象中封装了标签中的配置信息
  • 一个Servlet对应一个ServletConfig对象

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

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

  • ServletConfig接口的实现类是Tomcat服务器给实现的(Tomcat服务器说的就是WEB服务器)

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

    • public String getInitParameter(String name);//通过初始化参数的name获取value
      public Enumeration<String> getInitParameterNames();//获取所有初始化参数的name
      public ServletContext getServletContext();//获取ServletContext的对象
      public String getServletName();//获取Servlet的name
      
    • 以上方法可以在Servlet类当中,都可以使用this去调用,因为GenericServlet实现了Servlet接口

ServletContext

  • 一个Servlet对象对应一个ServletConfig

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

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

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

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

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

  • ServletContext接口常用的方法

    • public String getInitParameter(String name);//通过初始化参数的name获取value
      public Enumeration<String> getInitParameterNames();//获取所有初始化参数的name
      
    • <!--以上两个方法是ServletContext对象的方法,以下是配置信息-->
      <context-param>
          <param-name>pageSize</param-name>
          <param-value>10</param-value>
      </context-param>
      <!--注:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中-->
      <!--如果配置信息只是想给某一个servlet作为参考,那么配置到servlet标签中去即可,使用ServletConfig对象来获取-->
      
    • //获取应用根路径,可以动态获取应用的根路径
      //一般在java源码中不将应用的根路径写死
      public String getContextPath();
      //String contextPath = application.getContextPath();
      
    • //获取文件的绝对路径(真实路径)
      public String getRealPath(String path);
      
    • //通过ServletContext对象记录日志
      public void log(String message);
      public void log(String message, Throwable t);
      //Tomcat服务器的logs目录下:
      //catalina.2021-11-05.log 服务器端的java程序运行的控制台信息
      //localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储在这个文件中
      //localhost_access_log.2021-11-05.txt 访问日志
      
    • //ServletContext对象还有一个名字:应用域(还有其他域:如:请求域,会话域)
      //如果所有的用户共享一份数据,并且这个数据很少被修改,并且这个数据量很小,可以将这些数据放到ServletContext这个应用域当中
      //不是共享的数据将没有意义,因为ServletContext对象只有一个,只有共享的数据放进去才有意义
      //若数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁,大数据量会影响服务器的性能,占用内存小的数据量可以考虑放进去
      //所有用户共享的数据,如果涉及到修改,必然会存在线程并发所到来的安全问题
      //应用域相当于一个缓存
      //存
      public void setAttribute(String name, Object value);
      //取
      public void getAttribute(String name);
      //删
      public void removeAttribute(String name);
      
  • 注:编写Servlet类时,因为是B/S结构,这种系统数基于HTTP超文本传输协议的,所以应继承使用HTTPServlet,

  • 目前接触过的缓存机制:

    • 对内存当中的字符串常量池
    • 堆内存当中的整数型常量池【-128~127】
    • 连接池
      • java.sql.Connection对象
      • JVM是一个进程。MySQL数据库是一个进程。进程和进程之间建立连接,打开通道是很费劲的,跟消耗资源。所以可以提前创建好N个Connection连接对象,将连接对象放到一个集合当中,这就是连接池。通过使用连接池,大大提升了访问效率。
    • 线程池
      • Tomcat服务器本身就支持多线程。
      • 而且从创建开始即存在多线程,每个用户只是拿其中一个线程用而已。
      • WEB服务器都具有多线程。
    • redis
      • NoSQL数据库。非关系型数据库。缓存数据库
    • 向ServletContext应用域中存储数据,也等于将数据存放到缓存cache中

HTTP协议

  • 协议

    • 一套提前制定好的规范
    • 一套标准,有其他人或其它组织来制定
    • 可以沟通无障碍
  • HTTP协议

    • HTTP协议:W3C制定的一种超文本传输协议

    • W3C:

      • 万维网联盟组织
      • 负责制定标准的:HTTP HTML4.0 HTML5 XML DOM等规范
      • 万维网之父:蒂姆-伯纳斯-李
    • 超文本:

      • 不是普通文本,比如流媒体:声音、视频、图片等。
      • HTTP协议支持:
        • 传送普通字符串
        • 声音、视频、图片等流媒体信息
    • 这种协议游走在B和S之间,B向S发数据要遵循HTTP协议,S向B发数据童谣需要遵循HTTP协议。这样B和S才能解耦合。

    • 解耦合:

      • B不依赖S
      • S也不依赖B
    • B/S:浏览器访问WEB服务器的系统

    • HTTP协议:

      • 请求协议
      • 响应协议
    • HTTP协议就是提前制定好的一种消息模板

      • 不管什么浏览器,都这么发
      • 不管什么WEB服务器,都这么发
  • HTTP请求协议

    • 4部分:
      • 请求行
      • 请求头
      • 空白行
      • 请求体
  • HTTP响应协议(S–>B)

    • 4部分:

      • 状态行
      • 响应头
      • 空白行
      • 响应体
    • HTTP响应协议的具体报文:

      • HTTP/1.1 200 ok									状态行
        Context-Type: text/html;charset=UTF-8			响应头
        Context-Length: 160
        Data: Mon, 01 Nov 2022 00:00:00 GMT
        Keep-Alive: timeout=20
        Connection: keep-alive
        												空白行
        <!doctype html>									响应体
        <html>
        	<head>
        	</head>
        	<body>
        	</body>
        </html>
        
    • 状态行:

      • 三部分:

        • 第一部分:协议版本号(HTTP/1.1)

        • 第二部分:状态码(HTTP协议中规定的响应状态号,不同的响应结果对应不同的号码)

          • 20表示响应成功,这次结束

          • 404表示访问的资源不存在,通常是路径错误或者有可能是路径正确但是资源没启动成功。404错误是前端错误

          • 405表示前端发送的请求方式和后端请求的处理方式不一致

            • 如:前端POST请求,后端get方式处理
            • 如:前端GET请求,后端post方式处理
          • 500表示服务器端的程序出现了异常,一般会认为是服务器的错误导致的

          • 以4开始:浏览器错误导致

          • 以5开始:服务器端错误

  • 怎么查看协议内容:

    • 使用Chrome浏览器:F12,然后找到network,通过这个面板可以查看协议的具体内容
  • 怎么向服务器发送GET请求,怎么向服务器发送POST请求?

    • 目前为止,只有一种情况发送POST请求:使用form表单,并且form标签中的method属性值为:method=“post”
    • 其他所有情况一律都是get请求:
      • 在浏览器地址栏上直接输入URL,属于get请求
      • 在浏览器上直接点击超链接,属于get请求
      • form表单,form标签中没有写method属性,默认就是get
  • GET请求和POST请求区别:

    • get请求发送数据的时候,数据会挂在URL后面,并且在URL后面添加一个“?”,“?”后面是数据
    • post请求发送数据的时候,在请求体当中发送,不会回显到浏览器的地址栏上。也就是说post发送的数据,在浏览器地址栏上看不到
    • get请求只能发送普通的字符串,并且发送的字符串长度有限度,不同的浏览器限制不同,这个没有明显的规范
    • get请求无法发送大数据量
    • post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:视频,声音,图片
    • post请求可以发送大量数据,理论上没有长度限制
    • W3C中,get请求比较适合从服务器端获取数据
    • W3C中,post请求比较适合向服务器端传送数据
      • URI标识符,URL定位符
    • get请求是安全的,get请求是绝对安全的,因为get请求只是为了从服务器上获取数据
    • post请求是危险的,因为post请求是向服务器提交数据,如果这些数据通过后门的方式进入到服务器中,服务器时很危险的,另外post是为了提交数据,所以一般情况下拦截请求的时候,大部分会选择拦截(监听)post请求
    • get请求支持缓存
      • 任何一个get请求最终的“响应结果”都会被浏览器缓存起来。在浏览器的缓存中:一个get请求的路径对应一个资源
      • 实际上,只要发送get请求,浏览器会首先从本地浏览器缓存找,找不到之后再去服务器上获取,这种缓存机制的目的是为了提高用户的体验
      • 如果get请求不走缓存,而是直接查服务器:
        • 只要每一次get请求的请求路径不同即可
        • 在路径的后面添加一个每时每刻都在变化的时间戳
    • post请求不支持缓存
  • GET请求和POST请求如何选择?

    • 怎么选择?衡量标准?该请求是想获取服务器端的数据,还是想向服务器发送数据,如果是想从服务器上获取资源,建议使用GET请求,如果是想为了向服务器提交数据,建议使用POST请求
    • 大部分的form表单都是post提交
    • 如果表单有敏感信息,还是建议使用post请求
    • 做文件上传,一定是post请求,要传的数据不是普通文本
    • 其余的都可以用get请求

模板方法设计模式

  • 什么是设计模式?

    • 解决某个问题固定的解决方案(可以被重复使用)
  • 哪些?

    • GoF设计模式
      • 23种设计模式(Gang of Four:4人组提出的设计模式)
      • 举例模式
      • 工厂模式
      • 代理模式
      • 门面模式
      • 责任链设计模式
      • 观察者模式
      • 模板方法设计模式
    • JavaEE设计模式
      • DAO
      • DTO
      • VO
      • PO
      • pojo
  • 模板方法设计模式:

    • 在模板类的模板方法当中定义核心算法骨架,具体的实现步骤可以延迟到子类当中完成
  • 模板类通常是一个抽象类,模板类当中的模板方法定义核心算法

  • 模板类中的抽象方法就是不确定实现的方法,这个实现的事情交给子类

HttpServlet源码分析

  • HttpServlet类是专为HTTP协议准备的,比GenericServlet更适合HTTP协议下开发

  • HttpServlet在

    • jakarta.servlet.http.HttpServlet
  • 目前所接触到servlet规范的接口:

    • jakarta.servlet.Servlet 核心接口
    • jakarta.servlet.ServletConfig Servlet配置信息接口(接口)
    • jakarta.servlet.ServletContext Servlet上下文接口(接口)
    • jakarta.servlet.ServletRequest Servlet请求接口(接口)
    • jakarta.servlet.ServletResponse Servlet相应接口(接口)
    • jakarta.servlet.ServletException Servlet异常(类)
    • jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)
  • http包下有哪些类和接口?jakarta.servlet.http.*

    • jakarta.servlet.http.HttpServlet(HTTP协议专用Servlet类,抽象类)
    • jakarta.servlet.http.HttpServletRequest(HTTP协议专用请求对象)
    • jakarta.servlet.http.HttpServletResponse(HTTP协议专用响应对象)
  • HttpServletRequest封装了什么信息?

    • HttpServletRequest,简称request对象
    • HttpServletRequest封装了请求协议的全部内容
    • Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象中
    • 也就是说,只需要面向HttpServletRequest,就可以获取请求协议中的数据
  • HttpServletResponse对象是专门来响应HTTP协议到浏览器的

  • HttpServlet源码分析`

public class HelloServlet extends HttpServlet{
    //用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法
    public HelloServlet(){
    }
}
public abstract class GenericServlet implements Servlet,ServletConfig,java.io.Serializable{
    public void init(ServletConfig config) throws ServletException{
        this.config = config;
        this.init();
    }
    public void init() throws ServletException{
        //NOOP by default
    }
}
//模板类
public abstract class HttpServlet extends GenericServlt{
    @Override
    punlic void service(ServletRequset req, ServletResponse res) throws ServletException,IOException{
        HttpServletRequest request;
        HttpServletResponse response;
        try{
            //将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        }catch(ClassCastException e){
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        //调用重载的service方法
        service(request,response);
    }
    //这个service是一个模板方法
    //在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成
   protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
		//获取请求方式
       	//这个请求方式的可能是:“”
       	//注意request.getMethod()方法获取的是请求方式,可能是七种之一
       //GET POST PUT DELETE HEAD OPTIONS TRACE
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
}
  • 可以直接继承HttpServlet,重写其中的service()方法

    • 但是,享受到405错误,享受到HTTP协议专属的东西
  • Servlet类的开发步骤:

    • 第一步:编写一个Servlet类,直接继承HttpServlet
    • 第二步:重写doGet方法或者重写doPost方法,到底重写谁,Javaweb程序员说了算
    • 第三步:将Servlet类配置到web.xml文件当中
    • 第四步:准备前端页面(form表单),form表单中指定请求路径

关于一个web站点的欢迎页面

  • 什么是一个web站点的欢迎页面?

    • 对于一个webapp来说,可以设置
    • 设置了欢迎页面之后,当访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何“资源路径”,这个时候会默认这个欢迎页面
  • 怎么设置欢迎页面

    • 第一步:在IDE工具的web目录下新建一个文件login.html

    • 第二步:在web.xml文件中进行以下配置

      • <welcome-file-list>
            <welcome-file>login.html</welcome-file>
        </welcome-file-list>
        
      • 注意:设置欢迎页面,路径不需要以“/”开始,并且这个路径默认是从webapp的根下开始查找

    • 第三步:启动服务器,浏览器地址里输入地址

      • http://localhost:8080/serlvet
  • 一个webapp可以设置多个欢迎页面

    • 注意:越靠上的优先级越高,找不到的继续往下找
  • 若文件名设置为index.html,则不需要在web.xml文件中进行配置欢迎页面,因为在Tomcat服务器已经提前配置好了
    • 实际上配置欢迎页面有两个地方可以配置
      • 一个是在webapp内部的web.xml文件中(在这个地方配置的属于是局部配置)
      • 一个是在CATALINA_HOME/conf/web.xml文件中配置(在这个地方配置的属于是全部配置)
      • 注意:局部优先(就近原则)
  • 欢迎页可以是一个Servlet
    • 欢迎页就是一个资源

关于WEB-INF目录

  • 注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够用过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外

HttpServletRequest接口详解

  • HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest

  • HttpServletRequest接口是Servlet规范中的一员

  • HttpServletRequest接口的父接口是:ServletRequest

    • public interface HttpServletRequest extends ServletRequest()
      
  • HttpServletRequest接口的实现类是org.apache.catalina.connector.RequestFacade实现

    • public class RequestFacade implements HttpServletRequest{} 
      
  • HttpServletRequest对象是Tomcat服务器负责创建的。这个对象封装了HTTP的请求协议

    • 实际上是用户发送请求的时候,遵循了HTTP协议,发送的是HTTP的请求协议,Tomcat服务器将HTTP协议中的信息以及数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象当中,传给Javaweb程序员
    • Javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获取到请求的信息
  • request和response对象,一个是请求对象一个是响应对象。这两个对象只在当前请求中有效

  • HttpServletRequest接口中有哪些常用的方法

    • 怎么获取前端浏览器用户提交的数据

      • Map<String,String[]> getParameterMap()这个是获取Map
        Enumeration<String> getParameterNames()这个是获取Map中的所有key
        String[] getParameterValues(String name)根据key获取Map集合中的value
        String getParameter(String name) 获取value这个一维数组当中的第一个元素,这个方法最常用
        //以上的4个方法,和获取用户提交的数据有关
        
    • 跳转

      • 转发(一次请求)

        • //第一步:获取请求转发器对象
          RequestDispatcher dispatcher = request.getRequestDispatcher("/跳转网址");
          //第二部:调用转发器的forward方法完成跳转/转发
          dispatcher.forward(request,response);
          
      • 两个Servlet怎么共享数据?

        • 将数据放到ServletContext应用域当中,可以但是应用域范围太大,占用资源太多,不建议
        • 可以将数据放到request域当中,然后AServlet转发到BServlet,保证A和B在同一次请求当中,这样就可以多个Servlet共享数据
      • 转发的下一个资源必须是Servlet?

        • 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的
        • 注意:转发的时候,路径写法注意。转发路径以“/”开始,不加项目名
      • HttpServletRequest接口其他常用的方法:

        • //获取客户端的IP地址
          String remoteAddr = request.getRemoteAddr();
          //get请求在请求行上提交数据
          //post请求在请求体上提交数据
          //设置请求体的字符集(显然这个方法是处理POST请求的乱码问题)
          request.setCharacterEncoding("UTF-8");
          //响应中文
          response.setContentType("text/html;charset=UTF-8");
          //以上两种均在Tomcat10后改善
          
          //get请求乱码问题
          //方案:修改CATALINA_HOME/conf/server.xml配置文件
          <Connector URIEncoding = "UTF-8"/>
          //注意:从Tomcat8之后,URIEncoding默认值为UTF-8
          //获取应用根路径
          String contextPath = request.getContextPath();
          

在一个web应用中应该如何完成资源的跳转

  • 在一个web应用中应该如何完成资源的跳转,两种方式

    • 转发
    • 重定向
  • 区别:

    • 代码上:

      • 转发

        • //获取请求转发器对象
          RequestDisppatcher dispatcher = request.getRequestDispatcher("/dept/list");
          //调用请求转发器对象的forward方法完成转发
          dispatcher.forward(request,response);
          //合并
          request.getRequestDispatcher("/dept/list").forward(request,response);
          
      • 重定向

        • //注意:路径上需要加一个项目名
          //浏览器发送请求。请求路径是需要添加项目名的
          response.sendRedirect("/oa/dept/list")
          
    • 形式上:

      • 转发(一次请求)
        • 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet/a,最终请求之后,浏览器的地址还是当前地址
      • 重定向(两次请求)
        • 在浏览器地址栏上发送的请求是:http://localhost:8080/servlet/a,最终请求之后,浏览器的地址为http://localhost:8080/servlet/b
    • 本质区别:

      • 转发:有WEB服务器控制,跳转动作由Tomcat服务器内部完成
      • 重定向:浏览器完成
  • 如何选择?

    • 如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制
    • 剩下所有的请求均使用重定向(重定向使用较多)
  • 跳转的下一个资源要求不一定,需要是服务器内部合法的资源:Servlet、JSP、HTML…

Servlet注解,简化配置

  • 分析web.xml文件

    • web.xml文件容易庞大,最终占用内存过多
    • 在web.xml文件中进行Servlet信息的配置,显然开发效率比较低,每一个都需要配置
    • 而且在web.xml文件中的配置是很少被修改的,所以这些配置信息可以考虑直接写到java类当中。
  • Servlet3.0版本之后,推出了各种Servlet基于注解式开发

    • 开发效率高,不需要写大量的配置信息
    • web.xml文件体积变小了
    • 一些不会经常变化修改的配置建议使用注解,可能被修改的配置依旧写到web.xml文件中
  • 注解

    • jakarta.servlet.annotation.WebServlet
      
    • 在Servlet类上使用:@WebServlet,含有的注解属性:

      • name:指定Servlet的名字,等同于
      • urlPatterns:用来指定Servlet的映射路径,可以指定多个字符串:
      • loadOnStartUp:指定在服务器启动阶段是否加载该Servlet,等同于
      • value:和urlPatterns属性一致,如果注解的属性名是value,属性名也是可以省略的
      • 注意:不是必须将所有属性都写上,只需要提供需要的

分析使用纯粹Servlet开发web应用的缺陷

  • 在Servlet当中编写HTML/CSS/JavaScript等前端代码,存在问题:
    • 编写难度大,麻烦
    • 程序的耦合度高
    • 代码不美观
    • 维护成本高(难以维护)
  • 解决方案
    • 思路
      • 不写Servlet(Java程序),让机器自动生成,只需要编写Servlet程序中的“前端的代码”,然后让机器自动翻译这些代码为“Servlet这种java程序”,然后机器再自动将该程序编译生成class文件,然后再使用JVM调用这个class中的方法。

关于B/S结构系统的会话机制(session机制)

  • 会话:session

    • 用户打开浏览器,进行一系列操作,然后最终将浏览器关闭,这个整个过程叫做:一次会话。会话在服务器端也有一个java

      对象:session

  • 在java的servlet规范当中,session对应的类名:HttpSession

  • session机制属于B/S结构的一部分,如果使用PHP语言开发WEB项目,同样也有session这种机制,session机制实际上是一种规范,然后不同的语言对这种会话机制都有实现

  • session对象最主要的作用是:保护会话机制

  • 为什么要使用session对象来保存会话状态

    • 因为HTTP协议是一种无状态协议
      • 无状态:请求的时候,B和S是连接的,但是请求结束后,连接就断了。
      • 采用无状态协议,是为了减少服务器的压力
    • B,S断开,那么关闭浏览器这个动作,服务器是不知道的
  • 关于不使用request对象和ServletContext对象保存会话状态:

    • request.setAttribute(),request.getAttribute()。request是请求域,ServletContext是应用域
    • ServletContext对象域太大
    • request请求域(HttpServlet),session域(HttpSession),application域(ServletContext)
  • Cookie禁用了,session还能找到吗?

    • 可以,需要使用URL重写机制
    • URL重写机制会提高开发者的成本,开发人员编写任何请求路径的时候,后面都要添加一个sessionid,给开发带来了很大的难度,很大的成本,所以大部分的网站都是采用了禁止Cookie,则不予使用
  • 登录成功之后,可以将用户的登录信息存储到session中,也就是说session中如果有用户的信息就代表用户登陆成功,如果session中没有用户信息,表示用户没有登陆过,则跳转到登录页面。

    • 只是获取当前session,获取不到返回null

      • HttpSession session = request.getSession(false);
        
  • 销毁session对象:

    • session.invalidate();
      

Cookie

  • session的实现原理中,每一个session对象都会关联一个sessionid,如:
    • JSESSIONID=F95E9D50F2DE195851625F9FE238A541
    • 对于session关联的cookie来说,这个cookie是被保存在浏览器的“运行内存”当中
    • Cookie:JSESSIONID=D99AB4A3A8AC0E6AF58C8286ACDFA4F8
  • cookie最终是保存在浏览器客户端上
    • 可以保存在运行内存中(浏览器只关闭cookie就消失了)
    • 也可以保存在硬盘文件中(永久保存)
  • cookie作用:
    • cookie和session机制都是为了保存会话的状态
    • cookie是将会话的状态保存在浏览器客户端上
    • session是将会话的状态保存在服务器端上
    • 因为HTTP协议是无状态无连接协议。

JSP

  • JSP实际上就是一个Servlet

  • jsp文件第一次访问的时候比较慢

    • 大部分运维用户在给客户演示项目的时候,会提前将所有的jsp文件先访问一遍
    • 第一次比较麻烦:
      • 要讲jsp文件翻译生成java源文件
      • java源文件要编译生成class字节码文件
      • 然后通过class去创建servlet对象
      • 然后调用servlet对象的init方法
      • 最后调用servlet对象的service方法
    • 第二次比较快:
      • 因为第二次直接调用单例servlet对象的service方法
  • JSP是什么

    • JSP是java程序(JSP本质还是一个Servlet)
    • JSP:JavaServer Pages(基于Java语言实现的服务器端的页面)
    • Servlet是JavaEE的13个子规范之一,JSP也是JavaEE的13个子规范之一
    • JSP是一套规范,所有的web容器/web服务器都是遵循这套规范的,按照这套规范进行翻译
    • 每一个web容器/web服务器都会内置一个JSP翻译引擎
  • JSP要求:眼前是JSP代码,脑中是java代码

  • JSP的基础语法:

    • 在jsp文件中直接编写文字,都会自动被翻译到servlet类的service方法的out.write(“翻译到这里”)
    • JSP的page指令,解决响应时的中文乱码问题:
      • <%@page contentType=“text/html;charset=UTF-8”%>表示相应的内容是text/html,采用的字符集是UTF-8
    • 怎么在JSP中编写java程序:
      • <% java语句;%>
        • 在这个符号当中编写的被视为java程序,被翻译到Servlet类的service方法内部
      • <%! %>
      • 这个符号会将java程序编写到service方法之外
      • JSP就是servlet,servlet是单例的,多线程并发的环境,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题,所以很少用
      • 如果输出的内容中含有“java代码”:
        • <%= %>注意:在=后面编写要输出的内容
        • <%= %>会被翻译成java代码:out.print();
        • 翻译到service方法中
        • 当输出的内容中有动态变量是考虑<%= %>
    • 在JSP中如何编写JSP的专业注释
      • <%–JSP的专业注释,不会被翻译到java源代码中–%>
  • javabean其实就是java中的实体类,负责数据的封装

  • 由于Javabean符合javabean规范,具有更强的通用性

  • JSP的指令:

    • 指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件)

    • 指令包括:

      • include指令:包含指令,在JSP中完成静态包含,很少用
      • taglib指令:引入标签库的指令
      • page指令
    • 指令的使用语法:

      • <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值…%>
    • 关于page指令中常用的属性:

      • <%@page session="true|false"%>
        
      • <%@page contextType="text/json"%>
        contextType属性用来设置响应的内容类型
        但同时也可以设置字符集
        <%@page contextType="text/json;charset=UTF-8"%>
        
      • <%@page pageEncoding="UTF-8" %>
        pageEncoding="UTF-8"表示设置响应时采用的字符集
        
      • <%@page import="java.util.*"%>
        
      • <%@page errorPage="erro.jsp"%>
        当前页面出现异常之后,跳转到error.jsp页面
        
      • <%@page isErrorPage="true"%>
        表示启用JSP九大内置对象之一的:exception
        默认值是false
        

EL表达式

  • page指令,忽略EL表达式

    • <%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>全局变量
      也可以使用局部控制:\${username}忽略EL表达式
      
  • 获取应用的根:

    • ${pageContext.request.contextPath}
  • EL表达式中其他的隐式对象:

    • param

      • <%=request.getParameter("username") %>
        ${param.username}
        
    • paramValues

    • initParam

  • EL表达式的运算符

    • empty运算符的结果是boolean类型
    • ${empty param.username}
    • ${not empty param.username}
    • ${!empty param.password}

JSTL标签库

  • JSTL:

    • Java Standard Tag Lib(Java标准的标签库)
    • JSTL标签库通常结合EL表达式一起使用,目的是让JSP中的Java代码消失
  • 使用JSTL标签库的步骤:

    • 第一步:引入JSTL标签库对应的jar包。

      • tomcat10之后引入的jar包
        • jakarta.servlet.jsp.jstl-2.0.0.jar
        • taglibs-standard-impl-1.2.5.jar
        • taglibs-standard-spec-1.2.5.jar
      • 在IDEA怎么引入
        • 在WEB-INF下新建lib目录,然后将jar包拷贝到lib中,然后将其“Add Lib…”
        • 一定是要和mysql的数据库驱动一样,都是放在WEB-INF/lib目录下的
        • 如果这个jar是Tomcat服务器没有的,则需要将jar包放到WEB-INF/lib目录下
    • 第二步:在JSP中引入使用标签库(使用taglib指令引入标签库)

      • JSTL提供了很多种标签,重点掌握核心标签库

      • <%@taglib prefix="c" url="http://java.sun.com/jsp/jstl/core" %>
        
    • 第三步:适时使用标签

    • jstl中的核心标签库core中常用的标签:

      • c:if
        • <c:if test=“boolean类型,支持EL表达式”></c: if>
      • c:forEach
        • <c:forEach items=“集合,支持EL表达式” var=“集合中的元素” varStatus=“元素状态对象”>${元素状态对象.count}</c: forEach>
        • <c: forEach var=“i” begin=“1” end=“10” step=“1”></c: forEach>
        • c:choose c:when c:otherwise
    • 在前端HTML代码中,有一个标签,叫做base标签,这个标签可以设置整个网页的基础路径

      • 通常出现在head标签中

      • < base href=“http://localhost:8080/项目名/”>

      • 在当前页面中,凡是路径没有以“/”开始的,都会自动将base中的路径添加到这些路径之前

      • 需要注意:在JS代码中的路径,保险起见,最好不要依赖base标签,JS代码中的路径最好写上全路径

      • <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.scontextPath}/">
        

Filter过滤器

  • 公共代码冗余,没有得到复用:

    • 使用Servlet规范中的Filter过滤器来解决这个问题
  • Filter

    • 过滤器
    • Filter可以在Servlet这个目标程序执行之前添加代码,也可以在目标Servlet执行之后添加代码,都可以添加过滤规则
    • 一般情况下,都是在过滤器当中编写公共代码
  • 步骤:

    • 第一步:编写一个java类实现一个接口:jarkata.servlet.Filter,并且实现这个接口中的所有方法

      • init方法:在Filter对象第一次被创建之后调用,并且只调用一次
      • doFilter方法:只要用户发送一次请求,则执行一次,在这个方法中编写过滤规则
      • destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次
    • 第二步:在web.xml文件中对Filter进行配置,这个配置和Servlet很像

      • <filter>
            <filter-name>filter2</filter-name>
            <filter-class>filter2地址</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>filter2</filter-name>
            <url-pattern>*.do</url-pattern>
        </filter-mapping>
        
      • 或者使用注解:@WebFilter({“*.do”})

  • 注意:

    • Servlet对象默认情况下,在服务器启动的时候是不会新建对象的
    • Filter对象默认情况下,在服务器启动的时候会新建对象
    • Servlet是单例的,Filter也是单例的(单实例)
  • 目标Servlet是否执行,取决于两个条件:

    • 第一:在过滤器当中是否编写了:chain.doFilter(request,response);代码
    • 第二:用户发送的请求路径是否和Servlet的请求路径一致
  • chain.doFilter(request,response)l这行代码的作用:

    • 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet
  • 注意:Filter的优先级,天生的就比Servlet优先级高

    • /a.do对应一个Filter,也对应一个Servlet,那么一定是先执行Filter,然后再执行Servlet
  • 关于Filter的配置路径:

    • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配
    • /* 匹配所有路径
    • */do 后缀匹配,不要以 / 开始
    • /dept/* 前缀匹配
  • 在web.xml文件中进行配置的时候,Filter的执行顺序是依靠filter-mapping标签的配置位置,越靠上优先级越高。

  • 过滤器的调用顺序:遵循栈数据结构。

  • 使用@WebFilter的时候,Filter的执行顺序是:

    • 比较Filter这个类名
    • 比如:FilterA和FilterB,则先执行FilterA
    • 比如:Filter1和Filter2,则先执行Filter1
  • Filter生命周期:和Servlet生命周期一致

  • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化,Servlet不会

  • Filter过滤器有一个设计模式:责任链设计模式

    • 最大的优点:在程序编译阶段不会确定调用顺序,因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中的filter-mapping的顺序就可以调整Filter的执行顺序是在程序运行阶段动态组合的,那么这种设计模式被称为责任链设计模式
  • 责任链设计模式最大的核新思想:

    • 在程序运行阶段,动态的组合程序的调用顺序

Listener监听器

  • 监听器:
    • 监听器是Servlet规范中的一员,就像Filter一员,Filter也是Servlet规范中的一员
    • 在Servlet中,所有的监听器接口都是以“Listener”结尾
  • 作用:
    • 监听器实际上是Servlet规范留给程序员的特殊时机
    • 特殊时刻如果想执行这段代码,需要考虑使用对象的监听器
  • Servlet规范提供了哪些监听器:
    • jakarta.servlet包下:
      • ServletContextListener
      • ServletContext
      • ServletRequestListener
      • ServletRequestAttributeListener
    • jakarta.servlet.http包下:
      • HttpSessionListener
      • HttpSessionAttributeListener
      • HttpSessionBindingListener
      • HttpSessionIdListener
      • HttpSessionActivationListener
        • 监听:Session对象的钝化和活化
        • 钝化:session对象从内存储存到硬盘文件
        • 活化:从硬盘文件把session恢复到内存
    • 注意:所有监听器中的方法都是不需要程序员调用的,由服务器来负责调用?什么时候被调用?
      • 当某个特殊的事件发生(就是某个时机到了)之后,被web服务器自动调用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值