Javaweb——Servlet及HttpSeverlet的基础知识

1.创建一个Servlet 继承HttpServlet

(servlet就是服务器端的小组件)

image-20220321062541800

它有一个doPost方法,可以相应客户端发来的Post请求。

客户端给服务端发的请求,我们封装成request。

image-20220321063235496

重写doPost方法。

设置xml文件

image-20220321064035616

image-20220321064043266

Servlet的基础知识

Servlet是什么呢?Servlet就是一个服务端的小组件,这个小组件可以接受客户端发过来的信息,然后对其进行处理。

Servlet是一个抽象类,它的各种方法要由它的继承类和接口来实现。

继承关系

javax.servlet.Servlet接口

​ javax.servlet.GenericsServlet 抽象类

​ javax.servletHttpServlet

相关方法

image-20220321075028671

Servlet有下面三种方法:

  • init (初始化方法)
  • service(服务方法)
  • destory(销毁方法)

这里面对我们来说比较重要的二就是service方法,service就是服务方法,只要客户端发请求,service方法就会被调用,那么具体实现啥呢?

就得看它的实现类如何重写方法:

(javax.servlet.FenericsServlet抽象类 的service方法仍然是抽象的)

image-20220321075326441

仍然留给它的子类去实现

而HttpServlet就会去实现service方法了。(HttpServlet可以理解为专门处理Http文件的服务器组件Servlet)

Servlet里面其实有很多方法

image-20220326100954466

比如 doGet(),doPut(),doDelete()方法,这些只是把具体响应的方法代码抽象出来写成一个类,具体的过程是这样的。

就是如果客户端有信息发过来的话,HttpServlet会被调用的只有Servlet方法,然后Servlet方法就会去判断对面发过来的请求是什么?

如果对面是Post,那么我就先调用service,然后在service里判断是Post方法,所以我就调用doPost方法。相应的Get,Delete也是同样的道理。

如下图:

image-20220321075451095

String method = req.getMethod();

而如何获取方法是啥,这就和Http机制有关啦,Http每个方法都有名字,Get,Post等,HttpServlet这边获取方法的名字,然后再去判断。

接下来看一下doPost()的默认实现

image-20220321081315936

![](C:\Users\spaceman\AppData\Roaming\Typora\typora-user-images\image-20220321082153208.png

写的啥,大概意思就是出现错误时候,会报405错误吧。但是默认方法其实是不重要的,因为我们一般会去重写方法。

创建类继承HttpServlet

创建类继承HttpServlet

接下来通过一个例子来讲一下HttpServlet各种方法的关系。

当我们创建一个类继承HttpServlet的时候,这个类不就默认会继承父类的方法了吗?

(如果父类是抽象类,也就是它的方法是都没写的,那我们作为接口就必须实现它,而HttpServlet里的方法其实已经有默认实现

了,所以我们并不需要实现,但是我们可以去覆写,也是去重写写父类HttpServlet里的方法)(逐渐懂了java的继承实现关系)

那么我们现来就来试一试:

刚不是说了HttpServlet里面有service方法,而service方法里面会调用doGet,doPost方法吗?是的,就是这样。

那么现在比如我们重写doGet方法,那么会怎么样?如果方法我们重写了,那么调用时就会调用我们写的,没用重写的就调用父类的。

所以当我们重写doGet方法,而不重写其它方法时。首先,发送请求,然后service正常工作,因为我们没有重写service方法,然后如果客户端发过来的请求时get请求,那么就会调用我们重写的doGet方法。而如果客户端发过来的时Post请求,那么service方法就调用父类的doPost方法(因为我们没用重写,默认继承父类的)。

image-20220326103912001

image-20220326103946965

比如我们下面重写了doPost方法,但没用重写doGet方法,那么会报上面的错误。为什么呢?

我是这么理解,因为我们页面默认是get方法,也就是传get方法过去,而HttpServlet接收到也判断为get方法了

此时service方法准备调用get方法,而get方法是父类默认写的那个,而父类默认写的那个就会出现上面405的情况。

可能405是告诉我们说,我们没去重写HttpServlet的doGet方法吧。

现在我们重写一下doGet方法,看还会不会有同样的问题:

image-20220326104334367

image-20220326104343253

现在就不会出现405的问题了。

然后再让我们看一下重写Service方法,我们会发现重写了Service方法后,不管有没有重写doGet,doPost,都不会报405的错误了。

为什么呢?因为前面讲了,doGet,doPost是在service方法种被调用的,现在我们把service方法都覆写了,就不会再去调用doGet和doPost等了。

Servlet的生命周期

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

2.默认情况下:

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

重要结论:

==通过案例我们发现:servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。==也就是说对于一个HttpServlet类,tomcat只会创建一个实例。

Servlet的初始化时机

默认情况下,当有第一次请求时,Servlet才会去实例化,初始化,然后再服务。这样的好处时,提高系统的启动速度。

缺点是什么?第一次请求时,耗时较长。

因此得出结论,如果需要提高系统的启动速度,默认情况就适合了。

如果需要提高响应速度,我们应该设置Servlet的响应时机。

我们可以通过来设置servlet启动的先后顺序。在xml文件中设置。

image-20220321093854905

什么意思呢?也就是说我们是首先会新启动tomcat服务器,然后httpServlet和tomcat并不是同时的,我们可以设置httpServlet的启动时机,数字越小启动时间越靠前。

比如设置为0或1,那么在tomcat启动的同时,httpServlet也可以同时都启动了。这样的好处就是,第一个用户请求时,httpServlet早已经启动好了,所以很快就能响应。缺点就是可能启动的速度会变慢。

那么如果设置的数字大一点的话,也就意味着tomcat启动后,httpServlet不会立即启动,可能会等到用户请求时再启动,这样的好处时(tomcat)启动速度会很快,但是第一个用户请求时(需要启动httpServlet)速度会变慢,对第一个用户不太友好。

Servlet是线程不安全的

image-20220321095049068

什么叫做线程不安全,比如看上面的例子,

线程1在进入Servlet时,此时的num=1,所以它已经准备好了要调用if(num==1)的方法(还没进入if判断),

但是刚好此时线程2进来了,把num的值改了,改成了5,那么此时就会将num1的执行路径改了,会导致线程1执行的路径发生变化。

Servlet在容器中时:单例的,线程不安全的

单例:所有的请求都是同一个实例去响应。

线程不安全:一个线程根据变量值去做逻辑判断,但是在中间某个时间,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化。

我们已经知道了servlet时线程不安全的,启发是:尽量不要定义成员变量,如果不得不的定义成员变量的话,尽量不要对成员变量做一些逻辑判断。

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页面时,响应的内容就是<form…)

会话 session

1) Http是无状态的

  • HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
    • 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
      • 通过会话跟踪技术来解决无状态的问题。

2) 会话跟踪技术
– 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
– 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
– 常用的API:
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数相同
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

  session.getId() -> 获取sessionID
  session.isNew() -> 判断当前session是否是新的
  session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒,比如手机的中行软件,5分钟左右不点击就让你重新登陆(就是会话失效了)
  session.setMaxInactiveInterval()
  session.invalidate() -> 强制性让会话立即失效
  ....

image-20220321101130669

一般来说,同一个浏览器是同一个客户端,不同的浏览器是不同的客户端。

3) session保存作用域!

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

  • 常用的API:
    void session.setAttribute(k,v)
    Object session.getAttribute(k)
    void removeAttribute(k)

    3) session保存作用域

    • session保存作用域是和具体的某一个session对应的!!!
    • 常用的API:
      void session.setAttribute(k,v)
      Object session.getAttribute(k)
      void removeAttribute(k)

会话 session

1) Http是无状态的

  • HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
    • 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
      • 通过会话跟踪技术来解决无状态的问题。

2) 会话跟踪技术
– 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
– 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
– 常用的API:
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数相同
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

  session.getId() -> 获取sessionID
  session.isNew() -> 判断当前session是否是新的
  session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒,比如手机的中行软件,5分钟左右不点击就让你重新登陆(就是会话失效了)
  session.setMaxInactiveInterval()
  session.invalidate() -> 强制性让会话立即失效
  ....

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQ8WVt4r-1648276376251)(C:\Users\spaceman\AppData\Roaming\Typora\typora-user-images\image-20220321101130669.png)]

一般来说,同一个浏览器是同一个客户端,不同的浏览器是不同的客户端。

3) session保存作用域!

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

  • 常用的API:
    void session.setAttribute(k,v)
    Object session.getAttribute(k)
    void removeAttribute(k)

    3) session保存作用域

    • session保存作用域是和具体的某一个session对应的!!!
    • 常用的API:
      void session.setAttribute(k,v)
      Object session.getAttribute(k)
      void removeAttribute(k)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1L5cftii-1648276376252)(C:\Users\spaceman\AppData\Roaming\Typora\typora-user-images\image-20220321102838204.png)]

也就是说,可以在一个通过一个session来设置一个key value

然后通过相同的session就能取得这个key value

但是通过不同的session(不同客户端/浏览器)访问就是不行的。

Servlet的request和response参数

顺便在这里讲一下,HttpServlet的serive方法不是有两个参数吗?一个是request,一个是response。

request就是客户端发过来的http请求,我们可以从中获取各种从客户端发过来的参数。

比如客户端可以发过来各种参数,我们就可以通过request.getParameter来获取。

而response就是我们要对客户端做出的回应,可以通过客户端重定向等各种方式,详情请继续看下去。

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

服务器内部转发:getRequestDispatcher

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SmZ4WxU9-1648276376252)(D:\桌面文件\代码素材资料\Day4-servlet-http-session\素材\04.服务器内部转发.png)]

如果有客户端向服务器端发出请求,servlet可在服务器内部将该请求交给另一个Servlet去完成,这是服务器端的事,客户端感受不出这种变化。

  • 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的

    地址栏没有变化

image-20220321104325415

注意这里的重定向,里面的demo07 为什么就跳转到Demo07Servlet去呢?这就是取决于xml文件中的配置。

在这里插入图片描述

image-20220321104353726

客户端重定向 sendRedirect

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-toLyHxno-1648276376254)(D:\桌面文件\代码素材资料\Day4-servlet-http-session\素材\05.客户端重定向.png)]

这就是客户端发请求给服务器的servlet,但是servle把请求返回去,并告诉客户端去请求另一个servlet,客户端感受得到这种变化。

  - 两次请求响应的过程。客户端肯定知道请求URL有变化
  - 地址栏有变化

地址定向的问题

configuration的问题

这个问题很很很重要,是后面进阶知识的基础。如果搞不懂,很容易乱。

问题引入:首先,我们现在写了很多个servlet

image-20220326112656311

比如举刚刚上面覆写了service的例子,现在我们写了这个servlet,那么问题来了?为什么我们点击运行之后,浏览器访问的时候

就是这个servlet去响应,而不是其它servlet去响应呢?好问题。

现在先回到我们的configuration这里来,首先我们得先明白configuration到底帮我们实做了什么事。

image-20220326113048616

首先是部署,我的理解就是将对应的web模块里的artifacts的部署包部署到tomcat中,那么当tomcat运行时,就是执行该web模块中的代码,而不是执行执行其它模块。

image-20220326113142134

当我们点击开始运行的时候,编译器就会将我们的项目部署到tomcat上去,然后再打开URL的网址。(其实打开网址这一步不重要,只要项目已经部署到tomcat上,我们也是可以直接在地址栏输入网址访问的)

一般在计算机中默认8080号端口是服务器软件服务的端口,所以其实http://localhost:8080其实就是能找到服务器服务的地方,在这里就是能找到tomcat服务的地方。

而现在让我们看刚刚Deployment下面的Application context是什么意思,七十多它的意思可以理解成:我们写的这些代码要放在8080号端口后的什么位置。(大概理解就行,不是很准确)

我们可以很简单的写/,那么比如我们要访问服务器端的叫index的东西的时候,就直接访问http://localhost:8080/index 就可以了,而我们也可以写成/demo01,那么我们访问客户端的叫index的东西的时候,就要访问

http://localhost:8080/demo01/index。

这样理解了吧。所以Application context改了之后,那边的url也要改一下,默认打开的时候才能正常打开。

关于WEB文件夹的问题

就上面的问题继续讲,其实原理应该是这样的,熟悉tomcat的知道,tomcat有个webapps的文件夹,然后文件夹里面可以建一个文件夹,比如叫baidu,然后在baidu文件夹里建立一个WEB-INF的文件夹,可以暂时先不放东西,然后可以将我们的html代码等东西放入百度文件夹中,这样我们启动tomcat后,就可以通过http://localhost:8080/baidu/anewone.html 访问里面的页面了。

也就是说在文件夹baidu下的资源都能部署到服务器上了。

image-20220326123013153

而转头来看Intellij IDEA ,

image-20220326123219714

这个web文件夹其实就是对应刚刚讲到的baidu文件夹,里面有WEB-INF文件夹9放置一些配置文件),然后其它html文件等就要放在web文件夹下。

所以我们刚刚讲configuration的时候,我们可以将url设定为http://localhost:8080/demo01/22.html 就可以访问到该页面了。

那个Deployment下面的Application context其实就是说 访问22.html时要不要加什么前缀,这个意思(或者理解成web文件夹的路径,可以为/,也可以设置为/demo01)

关于定位Servlet的问题

总算是把前置知识讲完了,现在来讲关于定位servlet的问题。回到刚刚的问题

image-20220326123922541

刚刚举了上面覆写了service的例子,现在我们写了这个servlet,那么问题来了?为什么我们点击运行之后,浏览器访问的时候

就是这个servlet去响应,而不是其它servlet去响应呢?

这个首先得看配置文件.

image-20220326124026464

配置文件默认是打开/demo01/index,但是我们看了一下web文件夹里,并没有一个叫做”index“的文件呀。

我们再看一下我们的Demo01Servlet类,屏幕截图 2022-03-26 124223

会发现上面我们写了一个@WebServlet(“/index”),重点就在这个。

有没有发现什么问题?是的,就如你想的那样。(当然你也可能没想)

上面那句话的意思是啥呢?可以这么理解,就是将我们的Demo01Servlet定位一下,或者说给它起个名,然后隐式地将其放在web文件夹下,然后让客户端想找index的时候找得到。

也就是说,对于每个servlet来说,我们不将其放入web文件夹,但是我们要让客户端找的时候能找到,所以我们采取了两种方法:

一是上面讲的,在每个servlet上面价格@WebServlet(“/index”),注意要有斜杠,斜杠后面才是servlet的定位符。

二是写在xml配置文件中:

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

<servlet-mapping>
    <servlet-name>Demo01Servlet</servlet-name>
    <url-pattern>/index</url-pattern>
</servlet-mapping>

也是将Demo01Servlet和index 联系起来的方式。

在以后,我们所有对servlet的编程,大部分都是是用这个index。

我给它起了名,叫做servlet的定位符。这个定位符十分十分重要。

后面还会多次用到。

总结

总结一下:

如果是html文件的,直接放在web文件夹下,输入相应网址就能访问到。

如果是servlet的,要写一个定位符定位一下servlet,然后输入相应网址输入定位符

可以找到相应servlet,让相应的servlet来相应客户端的请求。

比如说我们上面的例子,我们打开网址是http://localhost:8080/demo01/index,这时候服务器就会自动去找和index对应的servlet,

然后让其相应客户端的请求。就是如此,理解了吧。


版权声明:本文为weixin_51818357原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。