浅入源码—Servlet篇
引入:Tomcat和servlet
在作者学习Tomcat和servlet之初,经常会搞混Tomcat和servlet的概念,所以在学习之前,我们一定要搞清楚什么是Tomcat,什么是servlet。
图片来自https://www.cnblogs.com/whgk/p/6399262.html
我们看出,servlet是部署在服务器上,服务器是载体,servlet是服务器上的工人。
Servlet内部结构
我们都知道servlet的核心是HttpServlet,那我们来看看类图:
ServletConfig接口
servlet配置对象,通过这个接口我们可以获得servlet相关配置参数,我们不重点关注这个类。
Servlet接口
这个是整个项目的老祖宗,最重要的部分是他的三个生命周期,init、service和destroy。
GenericServlet类
这是一个抽象类,因为里面有一个虚方法,这个方法在HttpServlet类中实现,具体作用我等下会讲
public abstract void service(ServletRequest req, ServletResponse res)
这个类实现了Servlet接口中除了service之外的所有方法。
父亲的三个周期中,GenericService类干了下面几件事:
-
实现init:主要是把配置文件载入,
public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
-
service:上面说了,只是把service写成了抽象方法,并没有实现它
-
destroy:空实现,啥也没干:
public void destroy() {}
其实这个类最重要的事情就是导入一些配置,这里我给大家补充一下,第一点的init方法我们给this.config赋了值,类中config声明是这样的:
private transient ServletConfig config;
transient关键字:一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。
然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
https://www.cnblogs.com/lanxuezaipiao/p/3369962.html
HttpServlet类
它的重头就是写service方法,而且它两个service方法。
-
protected void service(HttpServletRequest req, HttpServletResponse resp)
-
public void service(ServletRequest req, ServletResponse res)
ServletRequest是父亲,HttpServletRequest是儿子外部调用这个类的时候,因为2是public的,所以肯定执行2方法,2方法是这样的:
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
所以2方法就是一个桥梁,最后还得走1方法,至于为什么不直接用1方法,我的理解是:如果这个请求不是HTTP协议的话,那么执行1方法肯定会报错。所以我们要先判断这是不是HTTP协议。
这个方法会判断请求方法
String method = req.getMethod();
然后通过method执行特定的请求动作,比如doGet、doPost……那么开发者常用的,就是这个类中的各种doxxx方法了。让开发者自己决定请求来了的时候,我干啥、返回啥。
总结
从GenericServlet到HTTPServlet,其实就像是为HTTP请求量身定做的类,Generic告诉孩子你要实现一个service,其他的你别管,HTTPServlet告诉孩子(也就是我们),service需要doXXX的具体实现逻辑,你需要啥就写啥。
博客新手,肯定有许多地方不太准确,希望大家多提意见,我会逐步修改。