那么此时会有同学好奇了,这个service()方法到底是从哪里来的呢?
一般我们在编写servlet接口实现类的时候都会让这个servlet接口实现类去继承我们的HttpServlet类,而当我们点到HttpServlet源码的时候会发现HttpServlet继承自GenericServlet,然后
GenericServlet实体类实现了我们的Servlet接口,点到Servlet接口源码我们发现了我们最"原始"的service方法。
哈哈到这里我们所描述的语言可能还是有点太死板,并且会有很多同学会问到为什么要设计的这么麻烦,为什么不直接让我们自己编写的接口实现类直接去实现Servlet接口呢?
那样不是更加方便吗?那好现在我们来回答这个问题:
首先我们会发现我们所自己设计的接口实现类在被tomcat接口实现类创建实例对象的时候所调用的方法只有servic方法,那么先来看Servlet接口中的方法都有哪些?
此时我们看到,Servlet方法中只有service方法被用到了,其他方法都没有用到,那么假如我们所设计的接口实现类去实现Servlet接口的话,就要重写这个接口中的所有方法,而用到的只有service方法,这样就会显得代码很冗余,那么此时回到最开始我们所问的抽象类的作用是什么?就是****将接口中不需要使用的抽象方法教给抽象类进行完成 ,这样接口实现类只需要对接口中自己所需要方法进行重写就行了,那么我们的servlet也是这样做的,下面请看源码解析:
在源码中我们会发现,此时使用了****GenericServlet抽象类继承了Servlet接口
在GenericServlet抽象类中将Servlet接口中除掉service方法以外的所有方法都进行了重写,唯独没有将Servlet接口中service方法没有重写,至于为什么这么做,我们待会说,目的其实还是为了让我们的接口实现类去重写或者继承自己想要的方法。
小tip:在这里要注意一个点,如果一个非抽象类去继承我们的接口的时候是必须去重写接口中所有的抽象方法的,如果是一个抽象类去实现了某一个接口的话,可以不重写这个接口中所有的方法,只重写一部分方法也可。
此时我们会发现还有一个抽象类HttpServlet继承了GenericServlet类,为什么这么做呢?原因是我们想在HttpServlet这个类中去重写定义我们的service方法,从而让我们最终所自定义的servlet类无需自己定义service方法,从而去继承HttpServlet类中的service方法,并且在HttpServlet中的service方法会检验前台用来发送请求的HTTP方法(通过调用request.getMethod()获取 ),然后去调用相应的doGet,doPost等方法,而HttpServlet一共定义了有下面的七种方法来处理前台用来发送请求的HTTP方法,其中doGet方法和doPost方法尤为重要:
方法名称 | 重要性 |
doGet | 重要且常用 |
doPost | 重要且常用 |
doHead | 了解 |
doPut | 了解 |
doTrace | 了解 |
doOptions | 了解 |
doDelete | 了解 |
欧克到了这里我们来总结一下一个请求传输的流程:
这里我们就假定我们自己定义了一个OneServlet类来处理前台的请求,前台的请求方法为get:
当请求传到了后台的servlet实现类的时候,此时tomcat会自动调用语句Servlet oneservlet=new OneServlet();创建我们OneServlet的实例对象,然后再由我们的实例对象oneservlet执行
oneservlet.service()语句来调用我们的service方法,此时的这个service方法继承于父类HttpServlet中的方法,那么我们来看下父类HttpServlet中的service方法的实现吧:
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);
}
}
此处首先会使用 String method = req.getMethod();语句接收到前台所发来的方法,然后运用equals方法来挨个作比较,如果前台发来的是get方法的话,那么此处便去执行this.doGet(req, resp);语句,注意此处的this为当前对象的引用,而之前我们的实例对象oneservlet调用了此service方法,所以此处的this就是我们的实例对象oneservlet,等价于oneservlet这个实例对象去执行HttpServlet父类中的doGet方法,但是我们一般在我们自己实现的servlet实现类中都会重写父类HttpServlet中的doGet,doPost这两个常用的方法(其他五个用到的时候再重写就好,此处我们只先谈到doGet和doPost方法),所以此时oneservlet这个实例对象便会去执行自己所定义的doGet方法,执行完后将结果返回给前端。
3.3Servlet 的生命周期(以及 load-on-startup 的用法)
什么叫做生命周期,指的是类所创建出来的对象从创建到销毁的全过程。
servlet 的生命周期,指的是 servlet 对象从创建到销毁的全过程。
- 服务器启动,servlet 对象不创建。
- 浏览器发出请求,请求路径到达服务器中的项目,如果用到了指定的 servlet 来处理请求,则服务器会自动的为我们创建出来一个 servlet 对象来处理请求。
- servlet 对象创建完毕后,马上调用 init 方法,执行对象的初始化操作
- servlet 对象在创建完毕后,是以单例的形式存储在服务器上。第 2~n 次访问的时候,就不重新创建对象了,而是使用第一次访问时创建出来的单例对象。
- 浏览器发出的请求,由 servlet 中的 doGet/doPost 方法来处理请求。
- 关闭服务器,servlet 对象销毁。
- 在 servlet 对象销毁之前,调用 destory 方法执行最后的处理工作,servlet 对象被标记为垃圾回收,destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
- 加入load-on-startup 可以更改 servlet 对象的创建时机,由以前用到时才创建(懒汉)更改为启动服务器(此处指tomcat)就创建(饿汉)。(后续会讲到)
- servlet 中的单例其实是一种假单例。因为 servlet 对象没有进行构造方法私有化的操作,是能够在其他的类中随意创建的(我们自己手动创建 servlet 对象没有任何意义)。
四:Servlet请求处理过程图
我们知道url和servlet实例是要有对应的映射关系的,所以往往需要我们配置web.xml来进行映射:
假设此时我们使用OneServlet接口实现类来处理前台的请求的话,那么在web.xml文件中需要指定映射关系代码,代码如下:
OneServlet
com.bjpowernode.controller.OneServlet
OneServlet
/one
下面我们来挨个分析:
标签的作用:将Servlet接口实现类类路径地址交给Tomcat
OneServlet
com.bjpowernode.controller.OneServlet
等价于Tomcat 中有此句话: String OneServlet = "com.bjpowernode.controller.OneServlet
标签的作用:为了降低用户访问Servlet接口实现类难度,需要设置简短请求别名
OneServlet
< url-pattern>/one
好啦,讲完web.xml配置后我们再来将一个之前遗留的小问题:我们都知道我们的servlet接口实现类的实例对象只有当这个接口实现类检测到请求时才会创建,那么可不可以当http服务器启动的时候就去创建呢?
答案当然是可以,在web.xml配置文件中假如load-on-startup 便可以修改创建时间了,如下所示:
OneServlet com.bjpowernode.controller.OneServlet 30最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
mg-qpQf8UEj-1715572831343)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!